This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 2 years ago.
Goal
I am designing a boid system using Unity. I deal with the awareness radius by adding a boid to the "Swarm" list when it enters a collider. In order to find the force for each boid, I need to cycle through the swarm list, access the "Boid" class, and retrieve velocity and position.
Problem
The Boid classes from each swarm entity are added to a new list, and passed to the physics controller. However, a NullReferenceException is thrown at line 96, and I don't understand why that variable would be null. As far as I know, accessing a populated Enumerable<Boid> using foreach should have variables within.
NullReferenceException: Object reference not set to an instance of an object
Boid.Alignment (System.Collections.Generic.IEnumerable`1[T] boids) (at Assets/Scripts/Boid.cs:96)
Boid.Update () (at Assets/Scripts/Boid.cs:42)
After testing, it seems it is thrown when accessing any part of the new list of Boids.
Why does my new list contain no data? Is there a better way to handle a 2D implementation of boids in a 3D space? Is there a resource I can use to better understand Linq?
P.S. I am very new to using Linq systems, and most of this code is taken from this video and this Unity project script
Code
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Boid : MonoBehaviour
{
// Global Variables
public Boid_Settings settings;
// Local Variables
public Rigidbody body;
public Vector2 acceleration;
public Vector2 velocity
{
get
{ return new Vector2(body.velocity.x, body.velocity.z); }
set
{ body.velocity = new Vector3(value.x, body.velocity.y, value.y); }
}
public Vector2 position
{
get
{ return new Vector2(transform.position.x, transform.position.z); }
}
public List<GameObject> swarm = new List<GameObject>();
public List<GameObject> targets = new List<GameObject>();
// Functions
private void Start()
{
float angle = Random.Range(0, 2 * Mathf.PI);
transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
velocity = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
}
private void Update()
{
IEnumerable<Boid> boids = swarm.Select(o => o.GetComponent<Boid>()).ToList(); //Line 40
Vector2 alignment = Alignment(boids); //LINE 42
Vector2 separation = Separation(boids);
Vector2 cohesion = Cohesion(boids);
acceleration = settings.alignmentWeight * alignment + settings.cohesionWeight * cohesion + settings.seperationWeight * separation;
UpdatePhysics();
}
// Entity Awareness Assignment
private void OnTriggerEnter(Collider collider)
{
if (collider.CompareTag("Zombie"))
{ swarm.Add(collider.gameObject); }
else if (collider.CompareTag("Player") || collider.CompareTag("Lure"))
{ targets.Add(collider.gameObject); }
}
private void OnTriggerExit(Collider collider)
{
if (collider.CompareTag("Zombie"))
{ swarm.Remove(collider.gameObject); }
else if (collider.CompareTag("Player") || collider.CompareTag("Lure"))
{
targets.Remove(collider.gameObject);
StartCoroutine(LingerTarget(collider.gameObject));
}
}
IEnumerator LingerTarget(GameObject target)
{
targets.Add(target);
yield return new WaitForSeconds(settings.lingerTime);
targets.Remove(target);
}
// Core Boid Logic
public void UpdatePhysics()
{
// Apply the acceleration, and then limit the speed to the maximum.
Vector2 UncappedVelocity = velocity + acceleration;
velocity = ApplyLimit(UncappedVelocity, settings.maxSpeed);
float angle = Mathf.Atan2(velocity.y, velocity.x) * Mathf.Rad2Deg;
body.transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
}
private Vector2 Alignment(IEnumerable<Boid> boids)
{
Vector2 velocity = Vector2.zero;
if (!boids.Any()) return velocity;
foreach (Boid boid in boids)
{ velocity += boid.velocity; } //LINE 96
velocity /= boids.Count();
Vector2 steer = Steer(velocity.normalized * settings.maxSpeed);
return steer;
}
private Vector2 Cohesion(IEnumerable<Boid> boids)
{
if (!boids.Any()) return Vector2.zero;
Vector2 sumPositions = Vector2.zero;
foreach (Boid boid in boids)
{ sumPositions += boid.position; }
Vector2 average = sumPositions / boids.Count();
Vector2 direction = average - position;
Vector2 steer = Steer(direction.normalized * settings.maxSpeed);
return steer;
}
private Vector2 Separation(IEnumerable<Boid> boids)
{
Vector2 direction = Vector2.zero;
boids = boids.Where(o => Vector3.Distance(o.transform.position, position) <= settings.avoidanceRadius);
if (!boids.Any()) return direction;
foreach (Boid boid in boids)
{
Vector2 difference = position - boid.position;
direction += difference.normalized / difference.magnitude;
}
direction /= boids.Count();
Vector2 steer = Steer(direction.normalized * settings.maxSpeed);
return steer;
}
private Vector2 Steer(Vector2 desired)
{
Vector2 steer = desired - velocity;
steer = ApplyLimit(steer, settings.maxSteerForce);
return steer;
}
// Calculation Helpers
private Vector2 ApplyLimit(Vector2 baseVector, float limit)
{
if (baseVector.sqrMagnitude > limit * limit)
{ baseVector = baseVector.normalized * limit; }
return baseVector;
}
}
The Boid_Settings Module:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CreateAssetMenu]
public class Boid_Settings : ScriptableObject
{
// Boid
public float maxSpeed = 5;
public float avoidanceRadius = 1;
public float maxSteerForce = 3;
public float lingerTime = 2.5f;
public float alignmentWeight = 1;
public float cohesionWeight = 1;
public float seperationWeight = 1;
public float targetWeight = 1;
// Spawner
public float awarenessRadius = 2.5f;
}
Context Pictures
Proof the boid classes have data to be read
Unity Enviroment & Boid.cs Attachment
Unity Boid Boid.body Component
Each Boid finds two swarm mates when the game is run
Turns out I was assigning null data, because the GetComponent function at line 40 found the "Body" of the boids, which did not have a Boid script attached. When searching with GetComponentInChildren, the array is populated and values are given.
The Old Line:
IEnumerable<Boid> boids = swarm.Select(o => o.GetComponent<Boid>()).ToList();
The New Line:
IEnumerable<Boid> boids = swarm.Select(o => o.GetComponentInChildren<Boid>()).ToList();
Related
The code helps in moving the game object in a continousy loop. I want the randomized generated cubes to follow the same pattern too. I didn't add the condition for stopping the generation of game objects when it completes one round. Currently, the generated game objects don't move.
The ultimate idea is to generate splash scene. I would like to know if the following way is gpu efficient too!
using UnityEngine;
using System.Collections.Generic;
public class IntegratedScrpt : MonoBehaviour
{
public List<GameObject> splashImagesGOList;
public float InvokeRate = 10f;
public GameObject cube01;
private int selection;
// Loop mode variables
private Vector3 startPosition;
private Vector3 endPosition;
private float distance = 54f;
private float distanceCovered;
private float currentDistance;
//for Vector Lerp
private float currentLerpTime = 0f;
private float lerpTime = 9f;
private float t;
void Start()
{
startPosition = splashImagesGOList[1].transform.position;
Debug.LogError("selection VALUE AT" + selection);
endPosition = Vector3.back * distance;
}
void Update()
{
InvokeRepeating("pickpoints", 1.0f, InvokeRate);
//loop mode
distanceCovered = Vector3.Distance(startPosition, endPosition);
currentDistance = distanceCovered * t;
currentLerpTime += Time.deltaTime;
if (currentLerpTime == lerpTime)
{
currentLerpTime = lerpTime;
}
if (currentLerpTime > lerpTime)
{
currentLerpTime = 0f;
}
t = currentLerpTime / lerpTime;
Move();
if (currentDistance == 64)
{
splashImagesGOList[selection].transform.position = startPosition;
Move();
}
Debug.LogError("SELECTION" + selection);
}
// method for making the gameobjects move
public void Move()
{
splashImagesGOList[selection].transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
// code for instantiating the gameobjects
void pickpoints()
{
foreach (GameObject cube01 in splashImagesGOList)
{
int selection = UnityEngine.Random.Range(0, splashImagesGOList.Count);
// Instantiate(splashImagesGOList[selection], cube01.transform.position, cube01.transform.rotation);
Instantiate(splashImagesGOList[selection], startPosition, cube01.transform.rotation);
}
}
}
The reason the instantiated gameobjects aren't moving is because you're not assigning to any of their positions! splashImagesGOList[selection].transform.position is the position of one of the prefabs. In your current code, you instantiate the objects and then never interact with the instantiated objects.
You should have each object handle its own movement by separating out your movement logic into a different script and attaching the script to each of the prefabs in your list. You can use Mathf.Repeat to do the sort of looping your current code seems to mean to do.
Now, it's not clear what kind of pattern you are trying to achieve with the repeated simultaneous random instantiation but regardless of that, you probably don't mean to put InvokeRepeating in Update. Additionally, you should have some kind of end condition to cease the repeated PickPoints calls with CancelInvoke("PickPoints");. Creating an ever increasing number of objects is not gpu efficient ;)
Altogether, these changes might look like this:
public class SpashImageMover : MonoBehaviour
{
public Vector3 startPosition;
public Vector3 endPosition;
public float3 lerpTime;
private float t = 0; // in case code in Start is removed
void Start()
{
// remove these two lines if you don't want the objects synchronized
t = Mathf.Repeat(Time.time/lerpTime, 1f);
transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
void Update()
{
t = Mathf.Repeat(t + Time.deltaTime / lerpTime, 1f);
transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
}
public class IntegratedScrpt : MonoBehaviour
{
public List<GameObject> splashImagesGOList;
public float InvokeRate = 10f;
private int selection;
// Loop mode variables
private Vector3 startPosition;
private Vector3 endPosition;
//for Vector Lerp
private float lerpTime = 9f;
// end condition for PickPoints
private bool invokingPickPoints;
private float pickPointsTimeRemaining = 27f;
void Start()
{
startPosition = splashImagesGOList[1].transform.position;
Debug.LogError("selection VALUE AT" + selection);
endPosition = Vector3.back * distance;
InvokeRepeating("PickPoints", 1.0f, InvokeRate);
invokingPickPoints = true;
}
void Update()
{
if (invokingPickPoints)
{
pickPointsTimeRemaining -= Time.deltaTime;
if (pickPointsTimeRemaining <= 0 )
{
CancelInvoke("PickPoints");
invokingPickPoints = false;
}
}
}
// code for instantiating the gameobjects
void PickPoints()
{
foreach (GameObject cube01 in splashImagesGOList)
{
int selection = UnityEngine.Random.Range(0, splashImagesGOList.Count);
// Instantiate(splashImagesGOList[selection], cube01.transform.position, cube01.transform.rotation);
GameObject newGO = Instantiate(splashImagesGOList[selection], startPosition, cube01.transform.rotation);
SpashImageMover mover = newGO.GetComponent<SpashImageMover>();
mover.startPosition = startPosition;
mover.endPosition = endPosition;
mover.lerpTime = lerpTime;
}
}
}
As a sidenote, if you now find you don't like how you're instantiating the objects, that would be more appropriate for a different question with a very descriptive explanation of what you are trying to achieve. It's too broad of a question to try and address that here.
The camera is child of one soldier only and also the target is this soldier.
And the script attached to the camera.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public Transform target;
public float speed = 0.1f;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
transform.RotateAround(target.transform.position, new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
}
}
But now I want to do two things. To make the camera to rotate around the whole soldiers and not only the specific one. And also to make the camera stop slowly when it's facing the soldiers. Now the camera is behind when starting the game.
Using a bool flag if true to make the camera rotate around the soldiers until it's facing them then stop rotation and keep moving with the soldiers.
If unchecked false make the camera rotating around the solders none stop.
UPDATE what I tried so far:
This code will make it rotating around all the soldiers and it's working fine:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public float speed = 0.1f;
private List<GameObject> Soldiers = new List<GameObject>();
// Use this for initialization
void Start()
{
Soldiers.AddRange(GameObject.FindGameObjectsWithTag("Soldier"));
}
// Update is called once per frame
void Update()
{
RotateAround();
}
private void RotateAround()
{
transform.RotateAround(GetAverageLocationOfSoliders(), new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in Soldiers)
total += soldier.transform.position;
return total / Soldiers.Count(); // Assuming Soldiers is List<Soldier>
}
}
Then I tried to add a slowDown bool flag variable, But I messed it all and it's not working at all.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public float speed = 0.1f;
public bool slowDown = false;
private List<Vector3> SoldiersPositions = new List<Vector3>();
private List<Vector3> SoldiersFacingDirection = new List<Vector3>();
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void FixedUpdate()
{
RotateAround();
}
private void RotateAround()
{
var getSoldiers = GameObject.FindGameObjectsWithTag("Soldier");
foreach (GameObject soldier in getSoldiers)
{
SoldiersPositions.Add(soldier.transform.position);
SoldiersFacingDirection.Add(soldier.transform.forward);
}
var Center = GetAverageLocationOfSoliders();
var FacingDirections = GetAverageFacingDirectionOfSoldiers();
if (slowDown == true)
{
var D = transform.position - Center;
var CamAngle = Vector3.Angle(D, FacingDirections);
speed = speed - CamAngle;
}
transform.RotateAround(Center, new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
SoldiersPositions = new List<Vector3>();
SoldiersFacingDirection = new List<Vector3>();
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in SoldiersPositions)
total += soldier;
return total / SoldiersPositions.Count(); // Assuming Soldiers is List<Soldier>
}
private Vector3 GetAverageFacingDirectionOfSoldiers()
{
var total = new Vector3();
foreach (var soldierfacingdir in SoldiersFacingDirection)
total += soldierfacingdir;
return total / SoldiersFacingDirection.Count();
}
}
I'm not sure if the first code example only for the rotation is fine the way I wrote it. It's working but not sure if this is a good way to do the code ?
It seems to me that in the first code only the rotation the camera is a bit shaking or stuttering I mean the camera is not moving smooth when rotating around. It's almost hard to see in the game view in the editor but you can see it a bit in the scene view I think.
The reason I'm calling RotateAround(); in the Update is that the soldiers are in a move they are walking forward non stop.
How should I do the slowDown part ?
UPDATE 2:
This is the full coed now:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
[Header("Spin")]
public bool spin = false;
public Vector3 Direction;
[Range(0, 300)]
public float speed = 10f;
public bool randomSpeed = false;
public bool randomDirection = false;
[Range(0f, 100f)]
public float timeDirChange;
public Vector3 defaultDirection;
[Space(5)]
[Header("Move in circles")]
public bool moveInCircles = true;
public GameObject rotateAroundTarget;
public Vector3 axis;//by which axis it will rotate. x,y or z.
public float rotationSpeed; //or the speed of rotation.
public float upperLimit, lowerLimit, delay;// upperLimit & lowerLimit: heighest & lowest height;
public bool randomHeight = false;
public bool stopRotatingWhenFacing = false;
private float height, prevHeight, time;//height:height it is trying to reach(randomly generated); prevHeight:stores last value of height;delay in radomness;
[Space(5)]
[Header("Follow objects")]
public GameObject[] objectsToFollow;
public bool randomFollow;
private float nextRotationTime = 0f;
private int counter = 0;
private List<GameObject> Soldiers = new List<GameObject>();
// Use this for initialization
void Start()
{
Soldiers.AddRange(GameObject.FindGameObjectsWithTag("Soldier"));
}
private void Update()
{
if (randomSpeed)
{
speed = UnityEngine.Random.Range(0, 300);
}
if (spin)
{
if (randomDirection == false)
{
nextRotationTime = 0;
timeDirChange = 0;
Direction = defaultDirection;
}
else
{
if (Time.time > nextRotationTime)
{
nextRotationTime += timeDirChange;
RandomDirection();
}
}
transform.Rotate(Direction, speed * Time.deltaTime);
}
else
{
timeDirChange = 0;
randomDirection = false;
randomSpeed = false;
}
if (moveInCircles)
{
MoveInCircles();
}
}
private void RandomDirection()
{
Direction = new Vector3(UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1));
while (Direction == new Vector3(0, 0, 0))
{
counter++;
Direction = new Vector3(UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1));
if (counter == 2)
{
Direction = new Vector3(1, 0, 0);
break;
}
}
counter = 0;
}
private void MoveInCircles()
{
var F = GetAverageDirectionsOfSoliders();
var D = transform.position - GetAverageLocationOfSoliders();
var angle = Vector3.Angle(D, F);
if (angle < 5f)
{
rotationSpeed -= 0.1f;
}
transform.RotateAround(GetAverageLocationOfSoliders(), axis, rotationSpeed);
time += Time.deltaTime;
//Sets value of 'height' randomly within 'upperLimit' & 'lowerLimit' after delay
if (time > delay)
{
prevHeight = height;
if (randomHeight)
{
height = UnityEngine.Random.Range(lowerLimit, upperLimit);
}
time = 0;
}
if (randomHeight == false)
{
height = transform.position.y;
}
if (randomHeight)
{
//Mathf.Lerp changes height from 'prevHeight' to 'height' gradually (smooth transition)
transform.position = new Vector3(transform.position.x, Mathf.Lerp(prevHeight, height, time), transform.position.z);
}
else
{
transform.position = new Vector3(transform.position.x, height, transform.position.z);
}
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in Soldiers)
{
total += soldier.transform.position;
}
return total / Soldiers.Count(); // Assuming Soldiers is List<Soldier>
}
private Vector3 GetAverageDirectionsOfSoliders()
{
var totalf = new Vector3();
foreach (var soldier in Soldiers)
{
totalf += soldier.transform.forward;
}
return totalf / Soldiers.Count();
}
}
The RotateAround part is working fine:
transform.RotateAround(GetAverageLocationOfSoliders(), axis, rotationSpeed);
But the slow down part is not working. It's not slowing down at all when the camera is facing the soldiers.
This is how I calculate the average transform.forward vector of all the soldiers:
private Vector3 GetAverageDirectionsOfSoliders()
{
var totalf = new Vector3();
foreach (var soldier in Soldiers)
{
totalf += soldier.transform.forward;
}
return totalf / Soldiers.Count();
}
Then inside the MoveInCircles method I did:
var F = GetAverageDirectionsOfSoliders();
var D = transform.position - GetAverageLocationOfSoliders();
var angle = Vector3.Angle(D, F);
if (angle < 5f)
{
rotationSpeed -= 0.1f;
}
But it's never getting to the line:
rotationSpeed -= 0.1f;
To rotate around all the soldiers:
Get the center of the all the soldiers (sum the world position of all soldiers and divide by N number of soldiers)
Get the maximum distance of the soldiers from the center
Rotate the camera about the center, and ensure that it is further than the max distance from the center
To slow the camera down when facing the soldiers' front:
Average out the transform.forward vector of all the soldiers. We call this vector F. This is fair since all your soldiers will typically be facing in the same general direction for this request to even make sense.
Calculate the direction D, which is the direction from the soldiers' center to the camera. This is easy: D = camera.transform.position - soldiersCenter
Finally, find the acute angle between D and F, using Vector3.Angle(). If this angle is lower than a certain threshold, decrease the moveSpeed of the camera.
The actual code is easy to write, but I'll let you practice. Let me know if you need any help
From the sounds of it, it seems you have it already able to orbit around a single soldier, so do to more than one, simply take the average of their locations.
Pseudo code:
transform.RotateAround(GetAverageLocationOfSoliders(), ...);
...
private static Vector3 GetAverageLocationOfSoliders
{
var total = new Vector3();
foreach(var soldier in Soldiers)
total += soldier.transform.position;
return total / Soliders.Count(); // Assuming Soldiers is List<Soldier>
}
Now, Vector3 might not have stuff like Vector3 += Vector3 or Vector3 / int, but if that's the case just create your own methods were you do that manually (adding vectorA.x + vectorB.x, and vectorA.x / num, etc.
As far as making the camera stop when it's in front, that's a bit trickier. You might want to do some checks first to make sure they all have the same rotation, and then check, each Update, if the look at rotation of the camera will point to one of the soldiers.
But, if you want it to slow down, then, instead of checking if the lookAtRotation can point to a soldier, check if it can point to some offset of the camera's rotation, like so:
Pseudo Code:
lookRotation(transform.Rotation + offset) // Use something like this to find Soldiers[0]
Then, if that finds one, you can use a Lerp on your speed to lerp back down to a speed of 0.
You will also have to maintain a List<Soldier> or Soldier[] somehow.
I have created a script which was supposed to follow a specific path. Here is my script. But when I run my script nothing happens. Actually a capsule was supposed to move towards the child object of pathholder. I have tried using Vector3.movetoward but nothing happens
using System.Collections.Generic;
using UnityEngine;
public class Guard : MonoBehaviour {
public Transform pathholder;
private Vector3 startposition;
private Vector3 previousposition;
private float waittime=1.5f;
private Vector3[] waypoints;
Vector3 playerinitialposition;
void Start () {
Vector3[] waypoints = new Vector3[pathholder.childCount];
for (int x = 0; x < pathholder.childCount; x++) {
waypoints [x] = pathholder.GetChild (x).position;
}
startposition = pathholder.GetChild (0).position;
previousposition = startposition;
playerinitialposition = new Vector3 (startposition.x, this.transform.position.y, startposition.z);
this.transform.position = playerinitialposition;
StartCoroutine(Followpath(waypoints));
}
void Update () {
}
void OnDrawGizmos(){
foreach (Transform waypoint in pathholder) {
Gizmos.DrawSphere (waypoint.transform.position,1f);
Gizmos.DrawLine (previousposition, waypoint.position);
previousposition = waypoint.position;
}
}
IEnumerator Followpath ( Vector3[] waypoints){
int lengthinindex = 1;
Vector3 targetdestination = waypoints[lengthinindex];
while (true) {
Vector3.MoveTowards (this.transform.position, targetdestination, 5f * Time.deltaTime);
if (this.transform.position == targetdestination) {
lengthinindex = (lengthinindex + 1) % waypoints.Length;
targetdestination = waypoints [lengthinindex];
yield return new WaitForSeconds (waittime);
}
}
yield return null;
}
}
You need to assign the value returned by MoveTowards to your transform
transform.position = Vector3.MoveTowards (this.transform.position, targetdestination, 5f * Time.deltaTime);
You're calculating a new position with MoveTowards each time but never assigning it to the actual position of your object. MoveTowards Documentation.
I'm trying to get the distance between my player, and the nearest object with the tag 'wall' however I can't seem to get it to work.
To my knowledge my code isn't working at all.
So my question is;
What am I doing wrong? Again, I want to find the distance from my player and the nearest object with the tag 'wall'. If I'm near a object with the tag 'wall' I want it to set the variable to true.(nearWall = true) then once I'm away from the object(About 10.0f) I want it back to false.(nearWall = false)
This is the code I have been working with.
using UnityEngine;
using System.Collections;
public class PlayerMotor : MonoBehaviour {
private CharacterController controller;
private Vector3 moveVector;
private float speed = 2.0f;
private float verticalVelocity = 0.0f;
private float gravity = 12.0f;
private bool nearWall;
public GameObject playerObject;
GameObject closestObject;
float distance = Mathf.Infinity;
public float distanceToWall = Mathf.Infinity;
private void Start() {
nearWall = false;
playerObject = GameObject.Find("Player");
distanceToWall = 0;
controller = GetComponent<CharacterController> ();
}
public void getNearestWall()
{
if (distance <= 10.0f) {
nearWall = true;
print ("Near wall!");
}
else
nearWall = false;
}
GameObject findNearestWall()
{
GameObject[]objectArray;
objectArray = GameObject.FindGameObjectsWithTag("wall");
Vector3 position = playerObject.transform.position;
foreach(GameObject currentObject in objectArray)
{
Vector3 distanceCheck = currentObject.transform.position - position;
float currentDistance = distanceCheck.sqrMagnitude;
if (currentDistance < distance)
{
closestObject = currentObject;
distance = currentDistance;
}
}
return closestObject;
}
private void Update()
{
findNearestWall ();
moveVector = Vector3.zero;
if (controller.isGrounded)
{
verticalVelocity = -0.5f;
}
else
{
verticalVelocity -= gravity * Time.deltaTime;
}
if (Input.GetMouseButton (0)) {
if (!nearWall) {
if (Input.mousePosition.x > Screen.width / 2)
moveVector.x = speed;
else
moveVector.x = -speed;
}
else
{
moveVector.x = transform.forward.x * speed;
transform.Rotate(new Vector3(0, -90, 0));
}
}
moveVector.y = verticalVelocity;
moveVector.z = transform.forward.z * speed;
controller.Move (moveVector * Time.deltaTime);
}
}
One thing is that you are not calling getNearestWall() method - which is actually changing the flag - anywhere.
And second why don't you just try:
currentDistance = Vector3.Distance(currentObject.transform.position, position);
When calculating distance
first of all you need to call getNearestWall(); inside the Update() method ( after findNearestWall() of course ). also what you are doing now is getting the minimal distance the player reached in the whole game. you might want to add distance = Mathf.Infinity; in top of findNearestWall() so it will something like this:
GameObject findNearestWall()
{
GameObject[] objectArray;
objectArray = GameObject.FindGameObjectsWithTag("wall");
distance = Mathf.Infinity;
Vector3 position = playerObject.transform.position;
foreach (GameObject currentObject in objectArray)
{
Vector3 distanceCheck = currentObject.transform.position - position;
float currentDistance = distanceCheck.sqrMagnitude;
if (currentDistance < distance)
{
closestObject = currentObject;
distance = currentDistance;
}
}
return closestObject;
}
now whenever you get near a wall it should print Near wall!
note:
also you are calling FindObjectsWithTag() at an Update method which might significantly drain your processing power. you might want to avoid that by declaring a private GameObject[] objectArray in the class.
and then use objectArray = GameObject.FindGameObjectsWithTag("wall"); once at Awake() or Start()
This is Player script:
public class player
{ public projectile pro;
pro = GetComponent<projectile>();
void Update()
{
GameObject go = GameObject.Find("enemy");
Transform playerTransform = go.transform;
Vector3 posi = playerTransform.position;
pro.Target = posi; // getting error here
Instantiate(bulletprefab, position, Quaternion.identity);
}
}
This is Projectile Script:
Requirement: Get updated position of Target in Projectile but since projectileMotion() method is being called from Start() and I want the Target current's position at point when Instantiate is called in Player so as to calculate the Target_Distance appropriately in Projectile class,
But looks like the assignment from a Vector3 to _pro.Target which is a vector3 only is not allowed. Ho can I fix this?
public class projectile : MonoBehaviour
{
public Vector3 Target;
public GameObject bulletprefab;
public float firingAngle = 45.0f;
public float gravity = 9.8f;
public Transform Projectile;
private Transform myTransform;
void Awake()
{
myTransform = transform;
}
void Start()
{ myTransform.LookAt(Target);
StartCoroutine (ProjectileMotion ());
}
IEnumerator ProjectileMotion()
{
yield return new WaitForSeconds(0.25f);
Projectile.position = myTransform.position + new Vector3(0, 0.0f, 0);
// Calculating distance to target
float target_Distance = Vector3.Distance(Projectile.position, Target );
Debug.Log ("realUPDATEDOR not" + Target);
float projectile_Velocity = target_Distance / (Mathf.Sin(2 * firingAngle* Mathf.Deg2Rad) / gravity);
// X Y componenent of the velocity
float Vx = Mathf.Sqrt(projectile_Velocity) * Mathf.Cos(firingAngle * Mathf.Deg2Rad);
float Vy = Mathf.Sqrt(projectile_Velocity) * Mathf.Sin(firingAngle * Mathf.Deg2Rad);
// flight time since depends on horizontal component of velocity
float flightDuration = target_Distance / Vx;
// projectile rotated at target
Projectile.rotation = Quaternion.LookRotation(Target - Projectile.position);
float elapse_time = 0;
while (elapse_time < flightDuration) //looping and incrementing elapsed time
{
Projectile.Translate(0, (Vy - (gravity * elapse_time)) * Time.deltaTime, Vx * Time.deltaTime);
elapse_time += Time.deltaTime;
yield return null;
}
}
}
Target is present is Projectile class and is Vector3 only, how to fix this error?
the code you get a reference to the "projectile" script wont work.
change :
public projectile pro;
pro = GetComponent<projectile>();
to something like this:
public projectile pro;
void Start()
{
pro = GetComponent<projectile>();
}
Also as a suggestion you might wanna reduce the GameObject.Find uses inside the update function cause of its high cost.