I'm trying to animate / transform a gameObject movingObject from it's spawn position to the destination. I believe the issue is somewhere in the implementation of the IncrementPosition function.
Rather than moving the one cube from A to B. The script spawns multiple cubes until it gets to B. Can you see where I'm going wrong?
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class onClickSpawnMove : MonoBehaviour
{
public float speed;
public GameObject randomSpawn;
private GameObject movingObject;
private Vector3 destination;
void Start()
{
Spawn();
}
void Update()
{
SetDestination(movingObject.transform.position);
if (destination != movingObject.transform.position) {
IncrementPosition();
}
}
void IncrementPosition()
{
float delta = speed * Time.deltaTime;
Vector3 currentPosition = movingObject.transform.position;
Vector3 nextPosition = Vector3.MoveTowards(currentPosition, destination, delta);
movingObject.transform.position = nextPosition;
}
void Spawn() {
Vector3 spawnPosition = new Vector3(10, 0, 0);
movingObject = Instantiate(randomSpawn, spawnPosition, Quaternion.identity);
}
public void SetDestination(Vector3 value)
{
destination = new Vector3(20, 0, 0);
}
}
There are several ways to call the Spawn / Start method casually:
gameObject.AddComponent<onClickSpawnMove>(); the Start method will be invoked again.
Instantiate a prefab that onClickSpawnMove script is attached already.
gameObject.SendMessage("Start"); maybe, but rarely seen.
You can add a log or break point to check when the Spawn / Start method is called.
Print the hash code will help you know the different onClickSpawnMove instances.
And you can click the message in the console to know which GameObject is.
void Start()
{
Spawn();
Debug.Log("Start " + this.GetHashCode(), this);
}
Related
Here I share my AiPathfinding script. I want my AI walks to a destination (target) when game starts. however, when AI collide an certain object (finding with tag="bullet") then I want to set new destination. Although code does not give error, when playing AI goes to first destination then stops there even though it collides with certain object on the way.
Can someone have a look please?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class AIPathfinding : MonoBehaviour
{
[SerializeField] public Transform target;
[SerializeField] public Transform target2;
[SerializeField] public float speed = 200f;
[SerializeField] public float nextWayPointDistance = 3f;
[SerializeField] Path path;
[SerializeField] int currentWaypoint = 0;
[SerializeField] bool reachedEndOfPath = false;
[SerializeField] Seeker seeker;
[SerializeField] Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
InvokeRepeating("UpdatePath", 0f, 0.5f);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "bullet")
{
UpdatePath(target2);
}
}
public void UpdatePath(Transform target2)
{
if (seeker.IsDone())
{
seeker.StartPath(rb.position, target.position, OnPathComplete);
}
}
void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
void Update()
{
if (path == null)
{
return;
}
if (currentWaypoint >= path.vectorPath.Count)
{
reachedEndOfPath = true;
return;
}
else
{
reachedEndOfPath = false;
}
Vector2 direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized;
Vector2 force = direction * speed * Time.deltaTime;
rb.AddForce(force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if (distance < nextWayPointDistance)
{
currentWaypoint++;
}
}
}
My guess is that you never stopped the Coroutine InvokeRepeating("UpdatePath", 0f, 0.5f). So do call CancelInvoke() when the bullet hits and change the target of the seeker (Start InvokeRepeating(...) again).
I assume NavMeshAgent.SetDestination(Vector3 position) is called in the function seeker.SetTarget(). And somehow Path gets calculated and set.
You could also just move the body of Update Path into the Update function or call it from there. Nevermind the Interval. Or do call it in your custom interval. Just calculate the next time you want to call it so if the nextUpdatePathTime (Time.time plus your cooldown) is smaller than Time.time then call it and calculate the next nextUpdatePathTime.
I did it many times,but maybe i forgot.I am trying to instantiate prefab,and scale it down but it doesnt take any effects.It only works if I scale original prefab,but then I am not able to Destroy this prefab.
public class PozicijaButona : MonoBehaviour
{
Vector2 pozicijaV;
float x, y;
Text tekstPozicije;
List<Vector2> lista = new List<Vector2>();
List<Sprite> slike1t, slike2t, slike3t, slike4t;
public GameObject instantacija;
private GameObject oznacenaSl;
Vector3 skala;
// Start is called before the first frame update
void Start()
{
tekstPozicije = GameObject.Find("Canvas/pozicija").GetComponent<Text>();
List<Vector2> lista = new List<Vector2>();
slike1t= new List<Sprite>();
for (int i = 0; i < HintoviMale.slikeT.Count; i++)
{
slike1t.Add(HintoviMale.slikeT[i]);
}
skala = transform.localScale;
instantacija.transform.localScale = skala;
Debug.Log("sjaa" + skala);
}
// Update is called once per frame
void Update()
{
}
private void OnMouseDown()
{
pozicijaV = transform.position;
x = transform.position.x;
y = transform.position.y;
string xT = x.ToString();
string yT = y.ToString();
// tekstPozicije.text=xT+","+yT;
tekstPozicije.text = pozicijaV.ToString();
Destroy(oznacenaSl);
}
public void klikNaPoziciju()
{
Debug.Log("broj itema u slici"+HintoviMale.slikeT.Count);
oznacenaSl = Instantiate (instantacija, new Vector2(-0.7f, -3.4f), Quaternion.identity);
// oznacenaSl.transform.localScale = skala;
oznacenaSl.GetComponent<SpriteRenderer>().sprite=HintoviMale.slikeT[0];
}
}
I did it many times,but maybe i forgot.I am trying to instantiate prefab,and scale it down but it doesnt take any effects.It only works if I scale original prefab,but then I am not able to Destroy this prefab.
You never call your function named "klikNaPoziciju", which contains the Instantiate call. You also have commented out the line that changes the localScale of the object named "oznacenaSl".
Here is a simple example of instantiating an object and modifying it's localScale:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InstantiateObjectExample : MonoBehaviour
{
public GameObject objectToInstantiate;
private GameObject instantiatedObject;
private void Awake()
{
CreateInstanceOfObject();
}
private void CreateInstanceOfObject()
{
instantiatedObject = Instantiate(objectToInstantiate, transform.position, Quaternion.identity);
instantiatedObject.transform.localScale = new Vector3(2f, 2f, 1f);
}
}
In the Unity Hierarchy, create an empty gameObject and attach the script to it. Then in the Inspector for the gameObject you just created, drag your prefab into the "Object to Instantiate" field.
EDIT:
OP mentioned they are calling their public method from an OnClick method in the Unity Editor. I'm not familiar with that approach, but another approach would be to use the OnMouseDown() function in your script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InstantiateObjectExample : MonoBehaviour
{
public GameObject objectToInstantiate;
private GameObject instantiatedObject;
private void OnMouseDown()
{
CreateInstanceOfObject();
}
private void CreateInstanceOfObject()
{
Debug.Log("Creating instance!");
instantiatedObject = Instantiate(objectToInstantiate, transform.position, Quaternion.identity);
instantiatedObject.transform.localScale = transform.localScale;
}
}
For this to work, make sure the object you attach the script to has a collider attached to it. Clicking on the collider will trigger the OnMouseDown() function.
I'm a Unity novice and I'm creating a small 2D shooting game on a school project and I already managed to have a functional shooting system in the main character that even bounces off some objects using Unity Physics.
I now wanted to integrate an aim line that will predict the trajectory of the bullet, including the bounces on objects (bubble-shooter style).
I found a script that uses Raycast and Line Renderer and it supposedly does this and I tried to integrate it into my gun script but although it does not give any error, it simply does not show anything when I test the game. I do not know if the problem is in the settings that I put in the Line Renderer Component or it is in the Script.
Could someone help me understand where my error is and point the right way?
My goal is:
My Line Renderer Component Definitions:
My Weapon Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class Weapon : MonoBehaviour
{
[Range(1, 5)]
[SerializeField] private int _maxIterations = 3;
[SerializeField] private float _maxDistance = 10f;
public int _count;
public LineRenderer _line;
public Transform Firepoint;
public GameObject BulletPrefab;
public GameObject FirePrefab;
void Start()
{
_line = GetComponent<LineRenderer>();
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
}
_count = 0;
_line.SetVertexCount(1);
_line.SetPosition(0, transform.position);
_line.enabled = RayCast(new Ray(transform.position, transform.forward));
}
void Shoot()
{
//shooting logic
var destroyBullet = Instantiate(BulletPrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyBullet, 10f);
var destroyFire = Instantiate(FirePrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyFire, 0.3f);
}
private bool RayCast(Ray ray)
{
RaycastHit hit;
if (Physics.Raycast(ray, out hit, _maxDistance) && _count <= _maxIterations - 1)
{
_count++;
var reflectAngle = Vector3.Reflect(ray.direction, hit.normal);
_line.SetVertexCount(_count + 1);
_line.SetPosition(_count, hit.point);
RayCast(new Ray(hit.point, reflectAngle));
return true;
}
_line.SetVertexCount(_count + 2);
_line.SetPosition(_count + 1, ray.GetPoint(_maxDistance));
return false;
}
}
Unity has two physics engines one for 2D and one for 3D. The script you provided relies on the 3D physics engine and won't work for 2D colliders. I edited your script to function with 2D colliders.
Either way make sure that all your game objects use the same physics system. Also the original script only shows the line renderer if it hits something. Best of luck!
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class Weapon : MonoBehaviour
{
[Range(1, 5)]
[SerializeField] private int _maxIterations = 3;
[SerializeField] private float _maxDistance = 10f;
public int _count;
public LineRenderer _line;
public Transform Firepoint;
public GameObject BulletPrefab;
public GameObject FirePrefab;
private void Start()
{
_line = GetComponent<LineRenderer>();
}
// Update is called once per frame
private void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
}
_count = 0;
_line.SetVertexCount(1);
_line.SetPosition(0, transform.position);
_line.enabled = true;
RayCast(transform.position, transform.up);
}
private void Shoot()
{
//shooting logic
var destroyBullet = Instantiate(BulletPrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyBullet, 10f);
var destroyFire = Instantiate(FirePrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyFire, 0.3f);
}
private bool RayCast(Vector2 position, Vector2 direction)
{
RaycastHit2D hit = Physics2D.Raycast(position, direction, _maxDistance);
if (hit && _count <= _maxIterations - 1)
{
_count++;
var reflectAngle = Vector2.Reflect(direction, hit.normal);
_line.SetVertexCount(_count + 1);
_line.SetPosition(_count, hit.point);
RayCast(hit.point + reflectAngle, reflectAngle);
return true;
}
if (hit == false)
{
_line.SetVertexCount(_count + 2);
_line.SetPosition(_count + 1, position + direction * _maxDistance);
}
return false;
}
}
Well, I changed on the physics material of the bullet, the Friction to 0 and Bounciness to 1. Also on the rigidbody2D the linear drag, angular drag and gravity scale all to 0. Although it is not a perfect rebound it was very close to what I intend for the game. Thank you! Check it out: Game Gif
Only negative thing is that my bullet is not turning in the direction of the movement after the rebound. I tried to use transform.LookAt but didn`t work. This is my bullet script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float speed = 20f;
public Rigidbody2D myRigidbody;
// Start is called before the first frame update
void Start()
{
this.myRigidbody = this.GetComponent<Rigidbody2D>();
this.myRigidbody.velocity = transform.right * speed;
transform.LookAt(transform.position + this.myRigidbody.velocity);
}
}
But now i have this error: Error CS0034 Operator '+' is ambiguous on operands of type 'Vector3' and 'Vector2'
UNITY 2D C#
I have a Canvas object.
My Canvas is image (ARROW) that indicates target (STAR).
When a STAR hit the PLAYER,it is destroyed.
Unfortunately, I can not turn off the ARROW and (when STAR respawn) turn on it again, because after appearing ARROW indicates the previous target.
That's why I must to destroy the canvas.
I added a script to the STAR :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyTest : MonoBehaviour {
public SpawnStar other;
public GameObject Spawner;
public GameObject ToDestroy;
void Awake (){
GameObject Spawner = GameObject.Find ("Spawner");
other = Spawner.GetComponent<SpawnStar>();
}
void OnCollisionEnter2D (Collision2D coll){
if (coll.gameObject.tag == "Player") {
Destroy (gameObject);
Debug.Log("DestroyedStar");
GameObject ToDestroy = GameObject.Find ("Window_QuestPointer");
Destroy (ToDestroy);
Debug.Log("DestroyedOptionOne");
other.Start ();
}
}
}
I added a script to the CANVAS:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using CodeMonkey.Utils;
public class Window_QuestPointer : MonoBehaviour {
[SerializeField] private Camera uiCamera;
public SpawnStar other;
public GameObject Spawner;
private Vector3 targetPosition;
private RectTransform pointerRectTransform;
void Awake (){
GameObject Spawner = GameObject.Find ("Spawner");
other = Spawner.GetComponent<SpawnStar>();
other.Start ();
}
private void Start ()
{
targetPosition = GameObject.FindWithTag("Star").transform.position;
pointerRectTransform = transform.Find ("Pointer").GetComponent<RectTransform> ();
}
private void Update (){
Vector3 toPosition = targetPosition;
Vector3 fromPosition = Camera.main.transform.position;
fromPosition.z = 0f;
Vector3 dir = (toPosition - fromPosition).normalized;
float angle = UtilsClass.GetAngleFromVectorFloat(dir);
pointerRectTransform.localEulerAngles = new Vector3 (0, 0, angle);
float borderSize = 40f;
Vector3 targetPositionScreenPoint = Camera.main.WorldToScreenPoint (targetPosition);
bool isOffscreen = targetPositionScreenPoint.x <= borderSize || targetPositionScreenPoint.x >= Screen.width - borderSize || targetPositionScreenPoint.y <= borderSize || targetPositionScreenPoint.y >= Screen.height - borderSize;
Debug.Log (isOffscreen + " " + targetPositionScreenPoint);
if(isOffscreen){
Vector3 cappedTargetScreenPosition = targetPositionScreenPoint;
cappedTargetScreenPosition.x = Mathf.Clamp (cappedTargetScreenPosition.x, borderSize, Screen.width - borderSize);
cappedTargetScreenPosition.y = Mathf.Clamp (cappedTargetScreenPosition.y, borderSize, Screen.height - borderSize);
Vector3 pointerWorldPosition = uiCamera.ScreenToWorldPoint (cappedTargetScreenPosition);
pointerRectTransform.position = pointerWorldPosition;
pointerRectTransform.localPosition = new Vector3 (pointerRectTransform.localPosition.x, pointerRectTransform.localPosition.y, 0f);
}
else{
Vector3 pointerWorldPosition = uiCamera.ScreenToWorldPoint (targetPositionScreenPoint);
pointerRectTransform.position = pointerWorldPosition;
pointerRectTransform.localPosition = new Vector3 (pointerRectTransform.localPosition.x, pointerRectTransform.localPosition.y, 0f);
}
}
}
I added a script to the SPAWNER object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnStar : MonoBehaviour {
private int waveNumber = 0;
public int enemiesAmount = 0;
public GameObject star;
public GameObject option;
public Camera cam;
public GameObject objectToEnable;
// Use this for initialization
public void Start () {
StartCoroutine (StarEnable());
cam = Camera.main;
enemiesAmount = 0;
objectToEnable.SetActive (false);
}
// Update is called once per frame
public IEnumerator StarEnable () {
yield return new WaitForSeconds (10f);
float height = cam.orthographicSize + 1; // now they spawn just outside
float width = cam.orthographicSize * cam.aspect + 1;
if (enemiesAmount==0) {
Instantiate(star, new Vector3(cam.transform.position.x + Random.Range(-width, width),3,cam.transform.position.z+height+Random.Range(10,30)),Quaternion.identity);
enemiesAmount++;
Instantiate (option, transform.position, transform.rotation);
objectToEnable.SetActive (true);
}
}
}
In addition, the ARROW must respawn in the screen and the STAR off the screen.
you shouldn't destroy a canvas or any GameObject during runtime, answering your question, when you Instantiate the canvas prefab, set the renderer camera with
canvas.worldCamera, but you can, instead of destroying your canvas, create a container inside of it put all the GameObjects inside of this GameObject container, and activate and deactivate it when needed.
If you have one camera U can attach simple script which finds Canvas commonent and attach camera to it in Start() for example:
public class AttachCamera: MonoBehavour
{
private void Start()
{
gameObject.GetComponent<Canvas>().worldCamera = Camera.main;
}
}
If there are more than one camera U have to remember attached camera and set it mannualy after Canvas was spawned.
Can you please show your code that is responsible for destroying the Canvas?
I would use another GameObject that handles this gameplay logic.
First, I would use cameraObj.SetActive(false); to disable the camera, and not destroy it. Destroying and Creating objects can cause memory issues sometimes, and it's just not good practice unless absolutely necessary.
Then, use a Coroutine and WaitForSeconds() or something to that effect to wait, and then call cameraObj.SetActive(true) to re-enable your Camera's main GameObject.
i have a turret,as a game object when a enemy enters it's collison box,the turret starts firing towards it,the logic is when the enemy exits the collider,it should stop its firing ,and other problem is that when again an enemy enters the collison box i.e the second enemy,it gives me an exception ,"MissingReferenceException :the object of type 'transform' has been destroyed but you are still trying to access it.Your script should eihter be check if it is null or you should not destroy it",but i am checking if the list in not null in my code.here is my code
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class TurretScript : MonoBehaviour {
public float shotInterval = 0.2f; // interval between shots
public GameObject bulletPrefab; // drag the bullet prefab here
public float bulletSpeed;
private float shootTime = 0.0f;
private List<Transform> targets;
private Transform selectedTarget;
private Transform myTransform;
private Transform bulletSpawn;
void Start(){
targets = new List<Transform>();
selectedTarget = null;
myTransform = transform;
bulletSpawn = transform.Find ("bulletSpawn"); // only works if bulletSpawn is a turret child!
}
void OnTriggerEnter2D(Collider2D other){
if (other.tag == "enemy"){ // only enemies are added to the target list!
targets.Add(other.transform);
}
}
void OnTriggerExit2D(Collider2D other){
if (other.tag == "enemy"){
targets.Remove(other.transform);
Debug.Log("gone out");
}
}
void TargetEnemy(){
if (selectedTarget == null){ // if target destroyed or not selected yet...
SortTargetsByDistance(); // select the closest one
if (targets.Count > 0) selectedTarget = targets[0];
}
}
void SortTargetsByDistance(){
targets.Sort(delegate(Transform t1, Transform t2){
return Vector3.Distance(t1.position, myTransform.position).CompareTo(Vector3.Distance(t2.position, myTransform.position));
});
}
void Update(){
TargetEnemy(); // update the selected target and look at it
if (selectedTarget)
{
// if there's any target in the range...
Vector3 dir = selectedTarget.position - transform.position;
float angle = Mathf.Atan2(dir.y,dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);// aim at it
if (Time.time >= shootTime){// if it's time to shoot...
// shoot in the target direction
Vector3 lookPos = new Vector3(bulletSpawn.position.x,bulletSpawn.position.y,0);
lookPos = lookPos - transform.position;
float ang = Mathf.Atan2(lookPos.y,lookPos.x)*Mathf.Rad2Deg;
GameObject b1 = Instantiate(bulletPrefab,new Vector3(transform.position.x,transform.position.y,5),transform.rotation)as GameObject;
b1.rigidbody2D.velocity = new Vector3(Mathf.Cos(ang*Mathf.Deg2Rad),Mathf.Sin(ang*Mathf.Deg2Rad),0)*bulletSpeed;
shootTime = Time.time + shotInterval; // set time for next shot
}
}
}
}
here is my enemy script
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnemyScript : MonoBehaviour {
public Transform target;
public float speed = 2f;
public int Health;
public float GetHealth()
{
return Health;
}
void Update ()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
void TakeDamage(int damage){
Health -= damage;
if (Health <= 0)
Destroy(gameObject);
}
void OnTriggerEnter2D(Collider2D otherCollider)
{
PlayerControl shot = otherCollider.gameObject.GetComponent<PlayerControl>();
if (shot != null)
{
SpecialEffectsHelper.Instance.Explosion(transform.position);
Destroy(shot.gameObject);
}
}
}
you need to check if the selected target is the target leaving the collider. You remove the target from the targets list but the selectedTarget var is still populated.
For the null ref exception. Are you using Destroy() to kill the target? Destroy() doesn't cause an OnTriggerExit() or OnCollisionExit() event call, the object is just gone.
edit: you can get around the lack of a call by adding an OnDestroy() function to the dying object that sets it's position to something well outside the level/view of the player. This way the target leaves the collider and then disappears rather than just disappearing in place.