Right now I am making a timed "escape" themed game where the player must find pieces of their ship that have scattered upon crashing in order to get off the planet. The player must avoid the enemies that will end up chasing them throughout the level.
My current problem is figuring out how to get multiple enemies to follow once it is triggered.
Right now only the one follows but the other 5 in the area don't move. I was thinking about putting them into an array but I'm unsure if that will work as I need to access the navmeshagent component
Here is my code:
#pragma strict
//script by Kyle Crombie & Paul Christopher
//will add negative action if collision is detected with player
var target : Transform; //the enemy's target
var isSeen: boolean = false;
var agent: NavMeshAgent;
function OnTriggerStay() {
isSeen = true;
target = GameObject.FindWithTag("Player").transform; //target the player
}
function Update () {
if(isSeen){
agent.SetDestination(target.position);
}
}
You can change the type of the agent variable from NavMeshAgent to array of NavMeshAgent. In the editor you can set the size of the array and then assign all the enemies you want to react. Then you can iterate over them and update them all. This is what it looks like:
#pragma strict
//script by Kyle Crombie & Paul Christopher
//will add negative action if collision is detected with player
var target : Transform; //the enemy's target
var isSeen: boolean = false;
var agents : NavMeshAgent[]; // This is now an array!
function OnTriggerStay() {
isSeen = true;
target = GameObject.FindWithTag("Player").transform; //target the player
}
function Update () {
if(isSeen){
for (var agent in agents) {
agent.SetDestination(target.position);
}
}
}
Alternatively you could tag the enemies and use FindGameObjectsWithTag in combination with GetComponent. Then it looks like this:
#pragma strict
//script by Kyle Crombie & Paul Christopher
//will add negative action if collision is detected with player
var target : Transform; //the enemy's target
var isSeen: boolean = false;
function OnTriggerStay() {
isSeen = true;
target = GameObject.FindWithTag("Player").transform; //target the player
}
function Update () {
if(isSeen){
for (var agent in GameObject.FindGameObjectsWithTag("Enemy")) {
agent.GetComponent(NavMeshAgent).SetDestination(target.position);
}
}
}
Related
I have run into a bit of an issue while trying to get a scoring system into my game. I have a script on the camera to display the score via a GUI box. I want to check for collision of two other objects and then increment/decrement my score based off of the collision. I have "notes" falling down from the top of the screen and if they go off the bottom and hit the box tagged "miss" then it would decrease the score by some amount.
Script on Camera:
#pragma strict
var score: int = 0;
var customSkin : GUISkin;
function OnGUI()
{
GUI.skin = customSkin;
//Sets the background color of GUI objects to clear
GUI.backgroundColor = Color.clear;
GUI.Box(new Rect(770,25,150,60), "Score: " + score.ToString("0"));
}
Create script, attach it to your notes. Inside this script create OnCollisionEnter function and write your code to decrease score.
For example:
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.CompareTag("miss"))
{
ScoreManager.Instance.Score -= 10;
}
}
All "notes" and "miss" objects must have Collider component. And one of those (all "notes" or just "miss"-object) must have Rigidbody component.
Helpful link: Colliders and collisions
I have made a game where I spawn in several circles which shrink over time until they are supposed to vanish. The problem is, I make all of the circles with the instantiation function. This creates "Ball(clone)" and whenever I try to use Destroy(GameObject) to get rid of one of them i get the following error.
Can't destroy Transform component of 'Ball(Clone)'. If you want to destroy the game object, please call 'Destroy' on the game object instead. Destroying the transform component is not allowed.
To be clear, the creation of the balls is handled by one script attached to an empty child, and the destruction is another script attached to the ball. They are as follows.
var Xpos : float;
var Ypos : float;
var Ball : Transform;
//Place ball
function Update ()
{
if (Input.GetMouseButtonDown(0))
{
//debugging
Xpos = Input.mousePosition.x;
Ypos = Input.mousePosition.y;
//Get mouse input and convert screen position to Unity World position
var position : Vector3 = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Instantiate(Ball,Vector3(position.x,position.y,1),Quaternion.identity);
}
}
//delete ball
#pragma strict
var Ball : Transform;
function Update ()
{
Ball.animation.Play("Shrink");
}
function Despawn ()
{
Destroy(Ball);
}
The error message says it all; you can't Destroy() a Transform. You'll have to apply that to the GameObject instead.
Changing to Destroy(Ball.gameObject); should achieve just that.
I have a number of sprites on top of each-other on the game board. When i use the mouse and select the sprite on top, all sprites under the mouse position is selected. My question is how to only select the sprite that I click on and not catch the ones below?
Here is the code i am using for my tests, attached to the sprites:
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// print("ALL MousePosition: " + theActualMousePosition);
if(collider2D.OverlapPoint(theActualMousePosition)) {
// print("HIT theActualMousePosition: " + theActualMousePosition);
print(collider2D.gameObject.tag);
}
}
}
The results you are getting are completely expected as your code is placed on the GameObjects, what you should do is to push your script out of those objects or use another function than OverlapPoint (because overlap point does not check for collision it simply checks if you are in the bounds of an object, which means it is valid for all object)
Some ideas :
Using OnMouseDown should provide you with an event only for the first collider encountered
Using A raycast from the camera : Camera.ScreenPointToRay should be able to be used for a raycast and then to check only the first collider encountered
Using Layers depending on layer collision Order.
EDIT :
Or you could also cast the ray the other way around :
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = (transform.Position-theActualMousePosition).normalized;
Ray ray = Ray(transform.Position,direction)
if(Physics.Raycast(ray) == null) {
//Then there is nothing between the object and the camera then the object is the first one
print(collider2D.gameObject.tag);
}
}
}
I am working on an Unity3d project in which I want to dynamically add 3d objects to the scene, at the same position, orientation, upon a keyPress. For doing so, I imported these objects and made a prefab for all of these.. I am using the following script to do so.. bt it doesn't do the needfull.. The new object gets instantiated and gets added to the scene but the old objects don't get destroyed. And also ,the position of the new objects is not always the same. The scene consists of a Cube initially. After I press '1' ,'2' etc .. I want a new Object to replace the 1 being displayed (at the same position).
script :
var myCar : GameObject;
var Cube : GameObject;
var Globe: GameObject;
var Clock : GameObject;
var Pistol : GameObject;
var LCD : GameObject;
var prev : int;
var temp: GameObject;
function Start ()
{
prev =0;
temp=null;
}
function Update ()
{
if(prev==0)
temp=Cube;
else if(prev==1)
temp=myCar;
else if(prev==2)
temp=Globe;
else if(prev==3)
temp=Pistol;
else if(prev==4)
temp=Clock;
else if(prev==5)
temp=LCD;
if(Input.GetKey(KeyCode.Alpha1 || KeyCode.Keypad1))
{
if(prev!=1)
{
Instantiate(myCar,temp.transform.position ,temp.transform.rotation);
myCar.transform.localScale = Vector3(0.06,0.06,0.06);
Destroy(temp);
prev=1;
}
}
else if(Input.GetKey(KeyCode.Alpha2 || KeyCode.Keypad2))
{
if(prev!=2)
{
Instantiate(Globe,temp.transform.position ,temp.transform.rotation);
Globe.transform.localScale = Vector3(0.04,0.04,0.04);
Destroy(temp);
prev=2;
}
}
else if(Input.GetKey(KeyCode.Alpha3 || KeyCode.Keypad3))
{
if(prev!=3)
{
Instantiate(Pistol,temp.transform.position ,temp.transform.rotation);
Pistol.transform.localScale = Vector3(0.03,0.03,0.03);
Destroy(temp);
prev =3;
}
}
else if(Input.GetKey(KeyCode.Alpha4 || KeyCode.Keypad4))
{
if(prev!=4)
{
Instantiate(Clock,temp.transform.position ,temp.transform.rotation);
Clock.transform.localScale = Vector3(0.08,0.08,0.08);
Destroy(temp);
prev=4;
}
}
else if(Input.GetKey(KeyCode.Alpha5 || KeyCode.Keypad5))
{
if(prev!=5)
{
Instantiate(LCD,temp.transform.position ,temp.transform.rotation);
LCD.transform.localScale = Vector3(0.04,0.04,0.04);
Destroy(temp);
prev=5;
}
}
}
There are several things to consider:
It would be better to store a reference to the currently active object intead of the if-else-if part.
Destroy is called on the prefab objects but not on the existing GameObject instance and thus doesn't do anything.
The same for all manipulations of transform. They affect the prefabs only.
Not really a bug but really an enhancement: Scale your models in our 3D software so that they can be instantiated without localScale adjusting.
UPDATE: If you don't have access to the original models of the 3D software like Blender, Maya, ... you can easily define the appropriate scale in the prefab. The keypoint is to minimise repeating stuff in your code and to put all kind of property settings into the prefab.
Putting this altogether, my suggestions:
Refine your original models like in 4.
Introduce a member variable to store a reference to the current object.
UPDATE: If you want a special object for example Cube initialised on start, you first drag it into the scene, and then drag the newly created Cube from hierarchy view to the varable current. So you see the cube first when starting the scene and your script knows that this is current when it has to replace it.
Create a method for object instantiation
I'm working in C# but it should be something like
var current : GameObject;
...
function ReplaceObject (prefab : GameObject, newKey : int) {
if (newKey != prev) {
GameObject newObject = Instantiate(prefab, current.transform.position,
current.transform.rotation);
Destroy(current);
current = newObject;
prev = newKey;
}
}
...
function Update ()
{
if(Input.GetKey(KeyCode.Alpha1 || KeyCode.Keypad1)) {
ReplaceObject (myCar, 1);
} else if(Input.GetKey(KeyCode.Alpha2 || KeyCode.Keypad2))
ReplaceObject (Globe, 2);
I have a problem that iv been trying to figure out for a couple of says and i cant seem to pin point the cause of it! I have created a first person shooter that consists of a few enemies on a small map. I have two scenes (the main menu and the game level). When my player dies it is takes to the main menu from which you can choose to play the game again. This then reloads the level again. The first time the game is run it runs without any problems. However when i doe and press the play game button again it returns to me a message which states the following "MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it." from the code below I can only see two types which are of GameObject. I have tried to remove the muzzleFlash to see if that is the issue however it makes no difference. I have unticked all the static boxes as i read that this may be the cause of the problem but this did not resolve the problem. this script below is attached to an enemy, I have a PlayerShot script attached to the FPS. Please could someone help?
// speed of the AI player
public var speed:int = 5;
// speed the ai player rotates by
public var rotationSpeed:int = 3;
// the waypoints
public var waypoints:Transform[];
// current waypoint id
private var waypointId:int = 0;
// the player
public var player:GameObject;
// firing toggle
private var firing:boolean = false;
// the Mesh Renderer of the Muzzle Flash GameObject
private var muzzleFlashAgent:GameObject;
/**
Start
*/
function Start()
{
// retrieve the player
player = GameObject.Find("First Person Controller");
// retrieve the muzzle flash
muzzleFlashAgent = GameObject.Find("muzzleFlashAgent");
// disable the muzzle flash renderer
muzzleFlashAgent.active = false;
}
/**
Patrol around the waypoints
*/
function Patrol()
{
// if no waypoints have been assigned
if (waypoints.Length == 0)
{
print("You need to assign some waypoints within the Inspector");
return;
}
// if distance to waypoint is less than 2 metres then start heading toward next waypoint
if (Vector3.Distance(waypoints[waypointId].position, transform.position) < 2)
{
// increase waypoint id
waypointId++;
// make sure new waypointId isn't greater than number of waypoints
// if it is then set waypointId to 0 to head towards first waypoint again
if (waypointId >= waypoints.Length) waypointId = 0;
}
// move towards the current waypointId's position
MoveTowards(waypoints[waypointId].position);
}
/**
Move towards the targetPosition
*/
function MoveTowards(targetPosition:Vector3)
{
// calculate the direction
var direction:Vector3 = targetPosition - transform.position;
// rotate over time to face the target rotation - Quaternion.LookRotation(direction)
transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
// set the x and z axis of rotation to 0 so the soldier stands upright (otherwise equals REALLY bad leaning)
transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
// use the CharacterController Component's SimpleMove(...) function
// multiply the soldiers forward vector by the speed to move the AI
GetComponent (CharacterController).SimpleMove(transform.forward * speed);
// play the walking animation
animation.Play("walk");
}
/**
Update
*/
function Update()
{
// calculate the distance to the player
var distanceToPlayer:int = Vector3.Distance(transform.position, player.transform.position);
// calculate vector direction to the player
var directionToPlayer:Vector3 = transform.position - player.transform.position;
// calculate the angle between AI forward vector and direction toward player
// we use Mathf.Abs to store the absolute value (i.e. always positive)
var angle:int = Mathf.Abs(Vector3.Angle(transform.forward, directionToPlayer));
// if player is within 30m and angle is greater than 130 (IN FRONT) then begin chasing the player
if (distanceToPlayer < 30 && angle > 130)
{
// move towards the players position
MoveTowards(player.transform.position);
// if not firing then start firing!
if (!firing) Fire();
}
// if player is within 5m and BEHIND then begin chasing
else if (distanceToPlayer < 5 && angle < 130)
{
// move towards the players position
MoveTowards(player.transform.position);
// if not firing then start firing!
if (!firing) Fire();
}
else
{
// patrol
Patrol();
// stop firing
firing = false;
}
}
/**
Fire at the player
*/
function Fire()
{
// toggle firing on
firing = true;
// check if still firing
while (firing)
{
// hit variable for RayCasting
var hit:RaycastHit;
// range of weapon
var range:int = 30;
// fire the ray from our position of our muzzle flash, forwards "range" metres and store whatever is detected in the variable "hit"
if (Physics.Raycast(muzzleFlashAgent.transform.position, transform.forward, hit, range))
{
// draw a line in the scene so we can see what's going on
Debug.DrawLine (muzzleFlashAgent.transform.position, hit.point);
// if we hit the player
if (hit.transform.name == "First Person Controller")
{
// inform the player that they have been shot
player.GetComponent(PlayerShot).Shot();
// play gunshot sound
audio.PlayOneShot(audio.clip);
// show muzzle flash for X seconds
muzzleFlashAgent.active = true;
yield WaitForSeconds(0.05);
muzzleFlashAgent.active = false;
// wait a second or two before firing again
yield WaitForSeconds(Random.Range(1.0, 2.0));
}
}
// wait till next frame to test again
yield;
}
}
this is the PlayerShot which destroys the gameobject.
// the sound to play when the player is shot
public var shotSound:AudioClip;
// the number of lives
public var lives:int = 3;
/**
Player has been shot
*/
function Shot ()
{
// play the shot audio clip
audio.PlayOneShot(shotSound);
// reduce lives
lives--;
// reload the level if no lives left
if (lives == 0)
{
// destroy the crosshair
Destroy(GetComponent(CrossHair));
// add the camera fade (black by default)
iTween.CameraFadeAdd();
// fade the transparency to 1 over 1 second and reload scene once complete
iTween.CameraFadeTo(iTween.Hash("amount", 1, "time", 1, "oncomplete", "ReloadScene", "oncompletetarget", gameObject));
}
}
/**
Reload the scene
*/
function ReloadScene()
{
// reload scene
Application.LoadLevel("MainMenu");
}
The script attached to the enemy along with the BasicAI is the SoldierShot Script which destroys the gameobject. below is the script.
public var ragdoll:GameObject;
/**
Function called to kill the soldier
*/
function Shot()
{
// instantiate the ragdoll at this transform's position and rotation
Instantiate(ragdoll, transform.position, transform.rotation);
Destroy(GetComponent(BasicAI));
// destroy the animated soldier gameobject
Destroy(gameObject);
}
It seems that you keep a reference to the destroyed GameObject (or am I wrong?).
As opposed to what happens in common C# (and probably javascript) programs that when you have a reference to an object, it will never be garbage collected, in Unity if you destroy the object all your references to it will go to null.
Easy fix, you could delete the gameObject in the "Game Level" and instantiate your soldier on the fly with:
var instance : GameObject = Instantiate(Resources.Load("Soldier"));
You just need to make a Resources folder in your project folder and put the prefab for your solder into it.