In my game a player can fire a weapon which will launch a projectile that automatically targets the enemy who is closest to a certain object. However, how can I stop the projectile from also targeting my own player? Is there any way to tag all other players with a certain tag, whilst leaving my own player with a different tag? The players are instantiated as prefabs when the game starts.
When launching the projectile, give it a reference to the player who shot it. Then, when calculating the closest player, check if the closest is the same as the one who shot it. If he is, choose the second closest.
public class Projectile : MonoBehaviour
{
public Player player = null;
private Player target = null;
private Player GetClosestPlayer(IList listOfPlayers)
{
Player closestPlayer = ...; // use your algorithm method here
if (player != null && player == closestPlayer)
{
// copy listOfPlayers and remove closestPlayer from it
return GetClosestPlayer(copyOfListOfPlayersWithoutPreviousClosest);
}
return closestPlayer;
}
void Update()
{
if (target != null)
// steer to target
else
target = GetClosestPlayer();
}
}
Related
I am following the 2D Roguelike tutorial from unity learn.
When testing my game I enter the play mode and everything is fine, when using the arrows to move the player, unity stops and I can't click at any of the buttons in the editor, and the animation stops.
Here is my player script :
//Delay time in seconds to restart level.
public float restartLevelDelay = 1f;
//Number of points to add to player food points when picking up a food object.
public int pointsPerFood = 10;
//Number of points to add to player food points whne picking up a soda object.
public int pointsPerSoda = 20;
//How much damage a player does to a wall whne chopping it.
public int wallDamage = 1;
//Used to store a refrence to the Player's animator component
private Animator animator;
//Used to store player food points total during level.
private int food;
//Start overrides the Start function of MovingObject
protected override void Start()
{
//Get a component reference to the Player's animator component
animator = GetComponent<Animator>();
//Get the current food point total stored in GameManager.instance between levels.
food = GameplayManager.instance.playerFoodPoints;
//Call the Start function of the MovingObject base class.
base.Start();
}
//This function is called when the behaviour becomes disabled or inactive.
private void OnDisable()
{
//When Player object is disabled, store the current local food total in the GameManager so it can be re-loaded in next level.
GameplayManager.instance.playerFoodPoints = food;
}
private void Update()
{
//If it's not the player's turn, exit the function.
if (!GameplayManager.instance.playersTurn) return;
int horizontal = 0; //Used to store the horizontal move direction.
int vertical = 0; //Used to store the vertical move direction.
//Get input from the input manager, round it to an integer and store in horizontal to set x axis move direction
int v = (int) (Input.GetAxisRaw("Horizontal"));
horizontal = v;
//Get input from the input manager, round it to an integer and store in vertical to set y axis move direction
vertical = (int) Input.GetAxisRaw ("Vertical");
//Check if moving horizontally, if so set vertical to zero.
if (horizontal != 0)
{
vertical = 0;
}
//Check if we have a non-zero value for horizontal or vertical
if (horizontal != 0 || vertical != 0)
{
//Call AttemptMove passing in the generic parameter Wall, since that is what Player may interact with if they encounter one (by attacking it)
//Pass in horizontal and vertical as parameters to specify the direction to move Player in.
AttemptMove<Wall>(horizontal, vertical);
}
}
//AttemptMove overrides the AttemptMove function in the base class MovingObject
//AttemptMove takes a generic parameter T which for Player will be of the type Wall, it also takes integers for x and y direction to move in.
protected override void AttemptMove<T>(int xDir, int yDir)
{
//Every time player moves, subtract from food points total.
food--;
//Call the AttemptMove method of the base class, passing in the component T (in this case Wall) and x and y direction to move.
base.AttemptMove<T>(xDir, yDir);
//Hit allows us to reference the result of the Linecast done in Move.
RaycastHit2D hit;
//If Move returns true, meaning Player was able to move into an empty space.
if (Move(xDir, yDir, out hit))
{
//Call RandomizeSfx of SoundManager to play the move sound, passing in two audio clips to choose from.
}
//Since the player has moved and lost food points, check if the game has ended.
CheckIfGameOver();
//Set the playersTurn boolean of GameManager to false now that players turn is over.
GameplayManager.instance.playersTurn = false;
}
//OnCantMove overrides the abstract function OnCantMove in MovingObject.
//It takes a generic parameter T which in the case of Player is a Wall which the player can attack and destroy.
protected override void OnCantMove<T>(T component)
{
//Set hitWall to equal the component passed in as a parameter.
Wall hitWall = component as Wall;
//Call the DamageWall function of the Wall we are hitting.
hitWall.DamageWall(wallDamage);
//Set the attack trigger of the player's animation controller in order to play the player's attack animation.
animator.SetTrigger("playerChop");
}
//OnTriggerEnter2D is sent when another object enters a trigger collider attached to this object (2D physics only).
private void OnTriggerEnter2D(Collider2D other)
{
//Check if the tag of the trigger collided with is Exit.
if (other.tag == "Exit")
{
//Invoke the Restart function to start the next level with a delay of restartLevelDelay (default 1 second).
Invoke("Restart", restartLevelDelay);
//Disable the player object since level is over.
enabled = false;
}
//Check if the tag of the trigger collided with is Food.
else if (other.tag == "Food")
{
//Add pointsPerFood to the players current food total.
food += pointsPerFood;
//Disable the food object the player collided with.
other.gameObject.SetActive(false);
}
//Check if the tag of the trigger collided with is Soda.
else if (other.tag == "Soda")
{
//Add pointsPerSoda to players food points total
food += pointsPerSoda;
//Disable the soda object the player collided with.
other.gameObject.SetActive(false);
}
}
//Restart reloads the scene when called.
private void Restart()
{
//Load the last scene loaded, in this case Main, the only scene in the game.
SceneManager.LoadScene(0);
}
//LoseFood is called when an enemy attacks the player.
//It takes a parameter loss which specifies how many points to lose.
public void LoseFood(int loss)
{
//Set the trigger for the player animator to transition to the playerHit animation.
animator.SetTrigger("playerHit");
//Subtract lost food points from the players total.
food -= loss;
//Check to see if game has ended.
CheckIfGameOver();
}
//CheckIfGameOver checks if the player is out of food points and if so, ends the game.
private void CheckIfGameOver()
{
//Check if food point total is less than or equal to zero.
if (food <= 0)
{
//Call the GameOver function of GameManager.
GameplayManager.instance.GameOver();
}
}
How can I fix this?
I probably tried everything.
Regards.
This feels like an infinite loop to me, which would probably lock up the game and editor.
The code you posted calls out to other methods not listed here... I would look for a place where you might be recursively calling a method, or maybe where you're meaning to call base.theMethod but instead called this.theMethod.
This is probably a stack overflow where a function is calling himself infinitely.
It can also be a Coroutine from the base class with a infinite loop.
However, we can't say exactly where in the code, because is missing some information like what's inside the parent class (from the comments I suppose is called MovingObject)
Whenever posting a code be sure to send only the necessary piece of code (maybe removing some useless comments) and giving precious information like the inheritance and if is an override function would be useful to give also his base implementation.
I'm working on a multiplayer game and I'm encountering a issue when 2 players are loading the same level using PhotonNetwork.LoadLevel().
When I start the game alone, I can control my player and everything is fine. But when we are 2 players, Player A is controlling Player B and vice-versa.
I check a lot of links on the internet these past few days, and I learned the concept of PhotonNetwork.IsMine which I thought would solve all of my problem but it seems to not working with me. Also, I'm using the new input system of Unity but I don't think the issue come from here.
Basically, what I'm doing is:
Instantiate a player (this happened twice since I have 2 players) which have a PlayerManager
Player Manager Get Instance of the local player and synchronize camera with the local player only if isMine = true
CameraManager creates input manager if the script is linked to the local player by using isMine
Link the main camera to this script when the gamemanager request it
Update camera rotation only when isMine is true (second protection)
Here is a piece of my code:
GameManager.cs (Holding by a Scene Object, so it is initially instantiated for everyone with the scene)
void Start()
{
if(PlayerManager.LocalPlayerInstance == null)
{
//Get player's team
string team = (string)PhotonNetwork.LocalPlayer.CustomProperties["Team"];
int indexPlayer = GetSpawnPosition();
//Spawn player depending on its team and its index in the players pool
if (team.Equals("Spy"))
{
PhotonNetwork.Instantiate(this.spyPrefab.name, SpySpawns[indexPlayer].position, SpySpawns[indexPlayer].rotation);
}
else if (team.Equals("Defender"))
{
PhotonNetwork.Instantiate(this.defenderPrefab.name, SpySpawns[indexPlayer].position, SpySpawns[indexPlayer].rotation);
}
}
}
PlayerManager.cs (Holding by the player, so not initially instantiated with the scene)
void Awake()
{
//Keep track of the localPlayer to prevent instanciation when levels are synchronized
if (photonView.IsMine)
{
LocalPlayerInstance = gameObject;
}
//Don't destroy this gameobject so it can survives level synchronization
DontDestroyOnLoad(gameObject);
}
private void Start()
{
//Get Manager of the camera of the player and attach to the local player
CameraLookFPS cameraFPSManager = gameObject.GetComponent<CameraLookFPS>();
if (cameraFPSManager != null)
{
//Ensure that we the local player is controlling its own camera
if(photonView.IsMine == true)
{
cameraFPSManager.SynchronizeWithLocalPlayer();
}
}
else
Debug.Log("This player is missing the CameraLookFPS component");
}
CameraManager.cs (Holding by the player, so not initially instantiated with the scene)
private void Start()
{
//Synchronize camera with local player on start for debug
if(SynchronizeOnStart)
{
SynchronizeWithLocalPlayer();
}
//Bind input for controlling the camera
BindingInput();
}
private void BindingInput()
{
// Prevent control is connected to Photon and represent the localPlayer
if (photonView.IsMine == false && PhotonNetwork.IsConnected == true)
{
return;
}
else
{
//Get Components
Input_Master = new InputMaster();
//Enable
Input_Master.Enable();
//Input binding
Input_Master.Player.Look.performed += context => MoveCamera(context);
Input_Master.Player.Look.Enable();
}
}
public void SynchronizeWithLocalPlayer()
{
if (photonView.IsMine == false && PhotonNetwork.IsConnected == true)
{
return;
}
Player_Camera = Camera.main.transform;
isSynchronized = true;
}
I tried to be clear enough, tell me if something is bad explained. I'll continue my research on my side and I'll keep you in touch if I find something.
Thanks in advance for your help!
Adrien
I finally found my solution. It appears that I already read about it but I have failed when trying to solve it.
So, like a lot of people, it is a Camera issue.
What I did is:
Create a game object holding the camera and put it as a child of the player gameobject
Deactivate the game object holding the camera
When instantiating the player, check if the player is the local player, using PhotonNetwork.IsMine. If yes, activate the gameobject holding the camera through script
If you have questions, send me a message!
Adrien
Okay so I'm working on like a hack and slash kind of game in Unity and I have an enemy object who I'd like have attack the player and deal damage using colliders. I have added an event to the enemy's attack animation that calls the function OnTriggerEnter(Collider other) that should deal the damage but I get a error saying "Object reference not set to an instance of an object" whenever I try it out. Does anyone have any ideas on how I could make this work?
This code snippet can be very helpful for you to have an idea. I made this in an old project and works perfectly.
In this case is a right leg kick animation.
NOTE: code snippet not tested. “Health” script source is not included.
public class MeleeAttackController : MonoBehaviour
{
public SphereCollider kickAttackSphere; // in this case the sphere collider must be child of the right foot
public float meleeAttackDamage = 50;
public AudioClip kickAttackClip;
CapsuleCollider m_capsule;
void Awake()
{
kickAttackSphere.isTrigger = true;
m_capsule = GetComponent<CapsuleCollider>();
}
void Event_KickAttack_RightLeg() // invoked by the kick animation events (in case you have many kick animations)
{
if(CheckSphereAndApllyDamage(kickAttackSphere))
{
if (kickAttackClip)
{
AudioSource.PlayClipAtPoint(kickAttackClip, weaponAttackSphere.transform.position); // play the sound
}
}
}
/// <summary>
/// Returns true if the sphere collider overlap any collider with a health script. If overlap any collider also apply a the meleeAttackDamage.
/// </summary>
bool CheckSphereAndApllyDamage(SphereCollider sphere)
{
// check if we hit some object with a health script added
Collider[] colliders = Physics.OverlapSphere(sphere.transform.position, sphere.radius, Physics.AllLayers, QueryTriggerInteraction.Ignore);
foreach (Collider collider in colliders)
{
if (collider == m_capsule) continue; // ignore myself
Health health = collider.GetComponent<Health>();
if (health)
{
// if the collider we overlap has a health, then apply the damage
health.TakeDamage(meleeAttackDamage, transform);
return true;
}
}
return false;
}
}
I am working in unity 5 on a game and I am currently getting this error:
"SetDestination" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.NavMeshAgent:SetDestination(Vector3)
EnemyMovement:Update() (at Assets/Scripts/Enemy/EnemyMovement.cs:25)
My code for the enemyMovement script is:
using UnityEngine;
using System.Collections;
public class EnemyMovement : MonoBehaviour
{
Transform player;
PlayerHealth playerHealth;
EnemyHealth enemyHealth;
NavMeshAgent nav;
void Awake ()
{
player = GameObject.FindGameObjectWithTag ("Player").transform;
playerHealth = player.GetComponent <PlayerHealth> ();
enemyHealth = GetComponent <EnemyHealth> ();
nav = GetComponent <NavMeshAgent> ();
}
void Update ()
{
if(enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
{
nav.SetDestination (player.position);
}
else
{
nav.enabled = false;
}
}
}
Now I know that there is almost an identical question here: Unity - "SetDestination" can only be called on an active agent that has been placed on a NavMesh. UnityEngine.NavMeshAgent:SetDestination(Vector3) However, the question does not seem to have a firm response and I have tried doing some of the responses and nothing appears to work. I will say that my game does work fine. However, I have added an ability that if the player presses 'B' then all of the enemies will die on the screen. When I press 'B' the enemies die but the system 'crashes' and I get that error message.
EnemyManager.cs:
public void Kill(int InstanceID)
{
if (EnemyManager.Instance.EnemyList.ContainsKey(InstanceID))
EnemyManager.Instance.EnemyList.Remove(InstanceID);
}
public void DeathToAll()
{
Dictionary<int, Object> TempEnemyList = new Dictionary<int, Object>(EnemyManager.Instance.EnemyList);
foreach (KeyValuePair<int, Object> kvp in TempEnemyList)
{
// kvp.Key; // = InstanceID
// kvp.Value; // = GameObject
GameObject go = (GameObject)kvp.Value;
go.GetComponent<EnemyHealth>().Death();
}
}
Enemyhealth.cs
public void Death ()
{
// The enemy is dead.
isDead = true;
// Turn the collider into a trigger so shots can pass through it.
capsuleCollider.isTrigger = true;
// Tell the animator that the enemy is dead.
anim.SetTrigger ("Dead");
// Change the audio clip of the audio source to the death clip and play it (this will stop the hurt clip playing).
enemyAudio.clip = deathClip;
enemyAudio.Play ();
// Kill the Enemy (remove from EnemyList)
// Get the game object associated with "this" EneymHealth script
// Then get the InstanceID of that game object.
// That is the game object that needs to be killed.
EnemyManager.Instance.Kill(this.gameObject.GetInstanceID());
}
The problem here is that while you're checking if enemyHealth.currentHealth > 0 before calling nav.SetDestination(), that value doesn't get changed when the enemies are killed by the player's ability. Instead, it seems that the flag isDead is set on the enemy, without affecting the actual health.
To ensure nav.SetDestination() doesn't get called when the enemy is dead (because it seems at that point, the enemy is no longer a valid active agent), just add an additional condition to your if statement:
if(enemyHealth.currentHealth > 0 && !enemyHealth.isDead && playerHealth.currentHealth > 0)
{
nav.SetDestination (player.position);
}
Hope this helps! Let me know if you have any questions. (You may need to make isDead public for this to work.)
An alternative solution is to make a property IsDead which returns whether enemyHealth.currentHealth > 0, and kill enemies by setting their health to 0. Then you don't need to have a health tracker and a separate isDead flag that must be explicitly set. That's more of a design decision though as it won't affect the functionality.
I was having a similar issue where my navmesh agent was not able to reach the setdestination() point ,the navmesh agent just stopped so what i was doing wrong was that my setdestination() point was bit high from navmesh and that point was becoming out of range the agent.
So I created a foot empty gameobject that was near to the navmesh "in height".
and my agent was then able to reach the point
Hello and thanks for reading this.
I've created a little 2D game in Unity and I'm still very new to Unity.
I tried long and hard to search and check a guild / tutorial about how to make my "monster" move from A -> B and when he reach B then move back again. This he needs to keep doing all the time.
The Monster has a Box Collider and a Rigidbody and a "Destroyer" script so that if you run into him, you die.
I would really love to get a little help about how to create the monster movement.
Thanks alot.
This is quite simple, basically what you're looking for is a patrol feature which can be used like so:
GameObject A: (Cube, Cube Collider, Trigger = true, Disabled Mesh, Tag=PatrolPoint)
GameObject B: (Cube, Cube Collider, Trigger = true, Disabled Mesh, Tag=PatrolPoint)
GameObject C: npc that moves.
You would then need to create a script called "Patrol" that will handle the generation and ID of a patrol point. This script will be attatched to both GameObject A, and B.
using UnityEngine;
using System;
public class Patrol : MonoBehavior
{
public int patrolID;
public GameObject FindNextPoint()
{
GameObject base;
foreach(GameObject go in GameObject.FindGameObjectsWithTag("PatrolPoint"))
{
if(base == null && go.GetComponent<Patrol>().patrolID != patrolID)
{
base = go;
}
if(go.GetComponent<Patrol>().patrolID == (patrolID) + 1) {
return go;
}
}
// Return the first object found in the scene that isn't this object.
return base
}
}
Next you would need to use the OnTriggerEnter() function of unity in your script that's attatched to the player (or npc moving)-- http://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
using UnityEngine;
using System.Collections;
public class NpcScript : MonoBehaviour
{
private Vector3 targetLocation;
void OnTriggerEnter(Collider other)
{
if(other.gameObject.tag == "PatrolPoint")
{
setWalkTo(other.gameObject);
}
}
void setWalkTo(GameObject go)
{
targetLocation = go.GetComponent<Patrol>().FindNextPoint().transform.position;
}
}
You can have as many PatrolPoints as you want, just make sure to set the PatrolID variable to something different on each of them, the character will walk to them in order.
--- You have to add your own movement code, if you need help with that, let me know. Just move the gameobjects position towards targetLocation