- Home /
Getting the player to retreat a certain distance from the player
Hi all,
This problem has been plaguing me for about 2 weeks now and I cant seem to crack it.
I have a simple state machine for the enemy. The enemy chases the player until the player reaches a light source. Once the player reaches a light source, the enemy retreats. This all works except i want the enemy to stop retreating a certain distance from the player.
At the moment, the enemy is retreating indefinitely until the player leaves the light in which the enemy once again, gives chase.
As you can see in the Retreat function below, I have tried a few methods but none seem to do exactly what I want.
Pastie here or code below: http://pastie.org/private/tjfb3r1xzemmm05dele0wa
Thanks for the help in advance, I really appreciate it.
using UnityEngine; using System.Collections;
public class DarkChase : MonoBehaviour {
public float maxAdvanceSpeedY = 1.0f;
public float maxAdvanceSpeedX = 1.0f;
public float maxRetreatSpeedY = 1.0f;
public float maxRetreatSpeedX = 1.0f;
public float minDistance = 5.0f;
//public float maxRetreatDistance = 100.0f;
//public float maxDistance = -10.0f;
public GameObject Player;
public GameObject theDark;
public PlayerControl thePlayerScript;
//public Transform Target;
// Use this for initialization
void Start () {
Player = GameObject.FindGameObjectWithTag("Player");
thePlayerScript = thePlayerScript.GetComponent<PlayerControl>();
}
// Update is called once per frame
void Update () {
if (PlayerControl.inLight == false)
Advance();
else if (PlayerControl.inLight == true)
Retreat();
}
public void Advance () {
print ("Advance");
Vector3 playerPosition = Player.transform.position;
Vector3 darkPosition = transform.position;
Vector3 difference = darkPosition - playerPosition;
difference.Normalize();
transform.Translate (
(difference.x * -maxAdvanceSpeedX), (difference.y * -maxAdvanceSpeedY), (difference.z * -1));
}
public void Retreat () {
print ("Retreat");
Vector3 playerPosition = Player.transform.position;
Vector3 darkPosition = transform.position;
Vector3 difference = darkPosition + playerPosition;
difference.Normalize();
transform.Translate (
(difference.x * +maxRetreatSpeedX), (difference.y * +maxRetreatSpeedY), (difference.z * +1));
float dist = Vector3.Distance(playerPosition, darkPosition);
if (Vector3.Distance (playerPosition, darkPosition) <= minDistance)
Debug.DrawLine (playerPosition, darkPosition, Color.yellow);
print ("Distance to player: " + dist);
//darkPosition = Vector2.MoveTowards (transform.position, playerPosition, maxAdvanceSpeedX * Time.deltaTime);
//transform.Translate (
//(maxDistance), (0), (0));
//Vector3 maxDistance = Vector3.Distance (playerPosition, darkPosition);
//if (maxDistance >= maxRetreatDistance)
//.Translate (
//(difference.x - maxRetreatDistance), (difference.y - 0.01), (difference.z - 0.01));
}
}
I'm sure it's messed up with all the tweak attempts; at least put it back to form like
Retreat()
float dist = (the Distance calc)
Debug.Log("Dist is: " + dist);
if(dist <= $$anonymous$$Distance) {
// do stuff, Translate, etc
}
and show the dist debug; if Enemy is still retreating once dist fails check, there's something else.
Answer by DwaynePritchett · Aug 08, 2015 at 02:35 PM
I would add a third "state" idle. You might even choose to make an Enum for the states. But, to fix this, go into update and change the retreat conditions.
void Update () {
if (PlayerControl.inLight == false && (Vector3.Distance(playerPos,transform.pos)> minDistance)) {
Advance();
}
else if (PlayerControl.inLight == true){
if(Vector3.Distance(playerPos,transform.pos) < maxRetreatDistance){
Retreat();
}
}else{
Idle();
//Idle = Rotate toward character and don't move? Or whatever you want.
}
}
While there's nothing wrong with this, I also don't see anything right about it in the sense that his current Update() looks fine to me; the state is either true or false and it's being checked appropriately. Update() is not going to call Retreat() if (!inLight)
He says it "$$anonymous$$eeps" retreating. So having a 2nd check against the distance should stop the enemy from continuing to retreat past his maxRetreatDistance. And, the purpose of an idle state is just to have an explicit situation for when the enemy is neither advancing or retreating. Perhaps, if it goes idle for X amount of time, he wants to to lose aggro all together etc. Anyways... that is the logic behind it.
Sorry for the confusion. Yes the code I supplied currently works as it should. When the player is in a safe point the enemy retreats. The problem is that a player can sit in a safe area indefinitely and wait for the enemy to retreat far enough away that the player can just run through the rest of the level without danger.
I added a quick check as suggested by DwaynePritchett and this has worked without the Idle state. Problem now is that it is constantly checking and translating the advance or retreat method.
Ive been thinking that I would like the enemy to retreat back to the edge of the camera view. and then Idle.
At the moment, im just using public variables to dictate where the enemy stops and advance.
Vector3 playerPosition = Player.transform.position; Vector3 darkPosition = transform.position;
if (PlayerControl.inLight == false && (Vector3.Distance(playerPosition, darkPosition) > $$anonymous$$Distance)) {
Advance();
}
else if (PlayerControl.inLight == true) {
if (Vector3.Distance(playerPosition, darkPosition) < maxRetreatDistance) {
Retreat();
}
I think my main problem is I dont know how to get him to a point and then just stop the translate.
Thats why Im thinking that as long as the enemy retreats back to the camera edge and then goes idle, this would work best.
Answer by sparkzbarca · Aug 09, 2015 at 03:09 AM
if your saying you dont like it running endless checks in that you can actually use trigger boxes around lights and OnTriggerEnter push bots back, OnTriggerExit send in the hordes.
The thing is if you do this you can actually sleep the monster and then use OnTriggerExit to wake it.
Up to you though.
if your saying the issue is as far as "get him to a point and then just stop the translate"
if your saying you dont know how to because it doesn't ever quite arrive. mathf.approximately is your friend. instead of saying if position == X do if mathf.approximately(X)
Approx allows you to be slightly innacurate to account for the fact that vector3 is a floating point, suffers floating point inaccuracies and so it lets you say if your really close thats good engouh.
alternatly just pick a point and go if (vector3.distance(monster.position, point)) < .1f i'm within a tenth of a meter to my idle area, thats fine, lets start idling.
if your saying you dont know how to pick a point.
cast a ray from monster to player and or light source.
rays actually have a method called ray.getpoint(float distance)
thats gets you the world point X distance along the line.
So casting a ray from monster to player and ray.getpoint(10); would get you the point in the world 10 meters from the monster in the direction of the player.
so say if you set origin to player, direction as player.position - light.position so you have a line starting at the player but traveling in the OPPOSITE direction of the light. and use getpoint(10).
you'll now have the point closest to the light within 10 feet of the player kind of thing.