How would you go about creating a spell/ability following an enemy?

Options
So I'm creating a warlocks warcraft3 mod type game, and I was thinking how I would do an ability that follows the target, like the one in Warcraft3 mod version.

For now I have only done RPCs for all the abilities, and then just synced their startposition of simple directional abilities. But how would you normally create something that is following a target? Would I use a photonview on the spell or is there a better way?

Comments

  • Dannark
    Options
    I've faced the same problem some time ago with a guided missile, first i've used photonview on the missile but turns out i got a lot of problem and I figured a much better way later on,

    So the idea is to keep using RPCs and fake some stuffs but still get good results right?
    Basically on the RPC you tell the missile what will be your target (implement some aiming system to make the missile know witch will be your target event before it been instantiated) yes it will looks like a target system but it ensure the missile is following the right target!

    once you receive the RPC you instantiate it normally and locally (it will instantiated in all players anyway and will follow the right player so you don't have to worry about send positions of the projetil because it would be a waste of banda.

    On the projetil, in order to make it follow some object you could do something like this:

    <pre class="CodeBlock"><code>
    public float speed = 3;
    public float turn = 1; // this is important, how much it can turn when following its target
    public int fromID;
    // photon view id from the player who have shooted this projetil (useful to calculate points and all that
    // stuffs

    public int targetID = -1;
    // this is the PhotonView ID on the world by default -1 means no target found, so
    //we can shoot it straightforward

    public int lifeTime = 4;
    // how many time it will be "alive" and following its target (the target should be able to avoid this projetil if
    // you want that of course

    public Transform enemyTarget;
    private Vector3 centerPivot = Vector3.zero;
    public GameObject explosionEffect;
    private Rigidbody rb;
    private bool isDestryoing = false;

    void Start () {
    rb = GetComponent<Rigidbody> ();
    if (targetID != -1) {
    if (PhotonView.Find (targetID)) {
    enemyTarget = PhotonView.Find (targetID).gameObject.GetComponent<Transform> ();
    if (enemyTarget.GetComponent<Rigidbody> ()) {
    //this is important because make this projetil follow the center of the object instead
    //of its feet
    centerPivot = (enemyTarget.GetComponent<Rigidbody> ().centerOfMass);
    }
    } else {
    targetID = -1;
    }
    }
    StartCoroutine (explode());
    }

    public IEnumerator explode(){
    yield return new WaitForSeconds (lifeTime);
    // implements the destruction logic, effects
    }


    void FixedUpdate (){
    if (rb == null)
    return;

    Vector3 direction = Vector3.zero;
    if (enemyTarget != null) {
    direction = enemyTarget.position - (centerPivot); //keep tracking target new position
    }else{
    direction = transform.position + transform.forward; // make it shoot forward
    }

    // following LOGIC
    if (isDestryoing == false) {
    rb.velocity = transform.forward * speed;

    //turns it to face the target based on the turn speed
    Quaternion targetRotation = Quaternion.LookRotation (direction - transform.position);
    rb.MoveRotation (Quaternion.RotateTowards (transform.rotation, targetRotation, turn));
    }
    }
    </code></pre>
  • Dannark
    Options
    I've faced the same problem sometime ago, first time i used a PhotonView on the projetil to update its possition but turns out that was a really bad idea to make this, so here is how i did:

    First thing first you don't want to waste banda so Keep using RPCs and instantiating normally and locally but on the RPC you pass the target photonID (make some logic to implement a aiming system or something like that to get your target photon ID) this ensure that all missile will follow the same target object and will make the same movements plus we are not updating its position at all witch is great!

    On the projetil, in order to make it follow some object you could do something like this:

    public float speed = 3;
    public float turn = 1; // this is important, how much it can turn when following its target
    public int fromID; 
    // photon view id from the player who have shooted this projetil (useful to calculate points and all that 
    // stuffs
    
    public int targetID = -1; 
    // this is the PhotonView ID on the world by default -1 means no target found, so 
    //we can shoot it straightforward
    
    public int lifeTime = 4;
    // how many time it will be "alive" and following its target (the target should be able to avoid this projetil if 
    // you want that of course
    
    public Transform enemyTarget;
    private Vector3 centerPivot = Vector3.zero;
    public GameObject explosionEffect;
    private Rigidbody rb;
    private bool isDestryoing = false;
    
    void Start () {
    	rb = GetComponent<Rigidbody> ();
    	if (targetID != -1) {
    		if (PhotonView.Find (targetID)) {
    			enemyTarget = PhotonView.Find (targetID).gameObject.GetComponent<Transform> ();
    			if (enemyTarget.GetComponent<Rigidbody> ()) {
    				//this is important because make this projetil follow the center of the object instead
    				//of its feet
    				centerPivot = (enemyTarget.GetComponent<Rigidbody> ().centerOfMass);
    			}
    		} else {
    			targetID = -1;
    		}
    	}
    	StartCoroutine (explode());
    }
    
    public IEnumerator explode(){
    	yield return new WaitForSeconds (lifeTime);
    	// implements the destruction logic, effects
    }
    
    
    void FixedUpdate (){
    	if (rb == null)
    		return;
    
    	Vector3 direction = Vector3.zero;
    	if (enemyTarget != null) {
    		direction = enemyTarget.position - (centerPivot); //keep tracking target new position
    	}else{
    		direction = transform.position + transform.forward; // make it shoot forward
    	}
    
    	// following LOGIC
    	if (isDestryoing == false) {
    		rb.velocity = transform.forward * speed;
    
    		//turns it to face the target based on the turn speed
    		Quaternion targetRotation = Quaternion.LookRotation (direction - transform.position);
    		rb.MoveRotation (Quaternion.RotateTowards (transform.rotation, targetRotation, turn));
    	}
    }
  • Novez
    Options
    But wont the position of the projectile be different depending on the client if one has a lag spike for example?
  • Dannark
    Options
    Novez said:

    But wont the position of the projectile be different depending on the client if one has a lag spike for example?

    That's shouldn't be a big problem even if in some clients it looks in a different position the damage will still be counted for those with no lag (who have shooted for example) and eventually the hp will be updated for everybody.

    Just think of it, once playerA shoots the projectile, it is spawned for others right? but lets say playerB took 2 seconds to instantiate it, (now you have a lot of projectiles clones in each client) in your logic damage what you can do is make the the very first projectile that hit its target send and RPC with the damage to everybody and also "close" for others projectiles don't apply any more duplicated damage.

    something like:
    [Effect script to apply damage and effects]
    OnRPC: [Projectile_id:X, hitted_playerID:Y, with_damage:Z ]
    >> apply damage
    OnRPC: [Projectile_id:X, hitted_playerID:Y, with_damage:Z ]
    >> ignore damage on Projectile_id:X

    Or if you don't like, make only the playerA who have shooted the damage in first place take care of the damage thing.
    Its just and idea, of course you can make different, but if you want to test, i can send you the mini project i did using this logic and try yourself.