Advertisement

Projectile is affecting the player itself, how to fix it?

Started by November 08, 2022 07:49 AM
5 comments, last by roopesh23 2 years, 1 month ago

Hello all, I am making a small MOBA game in which the main player attacks enemy with a fireball and enemy health decreases.

The problem I am facing is that when I am throwing fireballs, the main player's health is also decreasing. I have added collider on both fireball and player, and on enemy have called OnCollisionEnter() on player script as I am changing the tag of other player to 'Enemy' once both are in network.

I tried to shift spawn point ahead of the player but still this is the issue. You can see the script, this script is used across the network.

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.AI;

using Mirror;

using UnityEngine.Assertions;


public class Mage : NetworkBehaviour

{

private Transform targetedEnemy;

private bool enemyClicked = false;

private bool walking;

private Animator anim;

private NavMeshAgent navAgent;

private float nextShot;

private float timeBetweenShots = 2f;

private bool isAttacking = false;


[SyncVar(hook="OnHealthChanged")] private int health = 100;

private int fireDamage = 20;

private Vector3 startingPosition;


[SerializeField] private float shootDistance;

[SerializeField] private Transform spawnPoint;

[SerializeField] private GameObject fireballPrefab;

[SerializeField] private GameObject playerHalo;

[SerializeField] private TextMesh healthText;



public override void OnStartLocalPlayer()

{

playerHalo.SetActive(true);

tag = "Player";

}


// Start is called before the first frame update

void Start()

{

anim = GetComponent<Animator>();

navAgent = GetComponent<NavMeshAgent>();

Assert.IsNotNull(spawnPoint);

Assert.IsNotNull(fireballPrefab);

Assert.IsNotNull(playerHalo);

Assert.IsNotNull(healthText);

startingPosition = transform.position;


}


// Update is called once per frame

void Update()

{

healthText.text = health.ToString(); //visible to all


if(!isLocalPlayer)

{

return;

}


Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

RaycastHit hit;


if(Input.GetButtonDown("Fire2"))

{

if(Physics.Raycast(ray,out hit,100))

{

if(hit.collider.CompareTag("Enemy"))

{

targetedEnemy = hit.transform;

enemyClicked = true;

}

else{

isAttacking = false;

enemyClicked = false;

walking = true;

navAgent.destination = hit.point;

navAgent.Resume();

}

}

}


if(enemyClicked)

{

MoveAndShoot();

}


if(navAgent.remainingDistance <= navAgent.stoppingDistance)

{

walking = false;

}else{

if(!isAttacking)

walking = true;

}


anim.SetBool("IsWalking",walking);

}


private void MoveAndShoot()

{

if(targetedEnemy == null)

{

return;

}


navAgent.destination = targetedEnemy.position;


if(navAgent.remainingDistance >= shootDistance)

{

navAgent.Resume();

walking = true;

}


if(navAgent.remainingDistance <= shootDistance)

{

transform.LookAt(targetedEnemy);


if(Time.time > nextShot)

{

isAttacking = true;

nextShot = Time.time + timeBetweenShots;

CmdFire();

}


navAgent.Stop();

walking = false;

}

}


[Command]

void CmdFire()

{

anim.SetTrigger("Attack");

GameObject fireball = Instantiate(fireballPrefab,spawnPoint.position,spawnPoint.rotation) as GameObject;

fireball.GetComponent<Rigidbody>().velocity = fireball.transform.forward*3;


NetworkServer.Spawn(fireball);


Destroy(fireball,2.5f);

}


void OnCollisionEnter(Collision collision)

{

if(collision.gameObject.CompareTag("Fireball"))

{

TakeDamage();

}

}


void OnHealthChanged(int health,int updatedHealth)

{

healthText.text = updatedHealth.ToString();

}


void TakeDamage()

{

if(!isServer)

{

return;

}


health -= fireDamage;


if(health <= 0)

{

health = 100;

RpcRespawn();

}

}


[ClientRpc]

void RpcRespawn()

{

if(isLocalPlayer)

{

transform.position = startingPosition;


}

}

}


The physics system likely has some functionality to filter collisions, often implemented using bit fields. So you can have various groups of objects (e.g. for each faction in a game), and dis/enable collisions to be detected between them or not. Check the docs of your engine.

Otherwise you could add a variable to your fireball to set who has launched it, and then prevent damage on collision by checking this.

Advertisement

@joej Yes, I was thinking maybe I need to add a separate script for fireball.

@roopesh23 I haven't read in detail through your code (it is kind of hard due to how the formatting broke), but I can explain how we did fireballs in games previously. We've used 3 different entities in total:

  1. Used during casting animation, this is just an effect and a timer. Once timer runs out the entity dies.
  2. Used for phase where fireball is flying along the line. It causes no damage, just lighting and particle effect. Each time step a CCD is calculated whether we hit between now and next frame, and if we do, we destroy entity and instantiate last one.
  3. Impact, animation of fireball explosion with spherical collider - anything that intersect gets damage (if it is possible for the item to get damage). At this point you could also calculate the distance and factor that in for example. It seemed too confusing for gameplay for us - so we ended up giving full damage to each colliding object.

Each of the phases used different entity - SpellCasting, SpellRanged and SpellAOE.

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

I made the change, added the script, but its throwing error "NullReferenceException: Object reference not set to an instance of an object" at the point where mage.TakeDamage() is called.

Look at the scripts Fireball AND Mage, In Fireball.cs "Collision Detected" is printed but in Mage.cs at TakeDamage function, down below "inside the function" is not printed.

Why is the error, all objects are there, is something missing?

Everyone object has collider, fireball has trigger On, Tags are all added, but while hitting enemy its not triggering TakeDamage function.

Fireball.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;

public class Fireball : NetworkBehaviour
{
    private Mage mage;
    
    void OnTriggerEnter(Collider collider)
    {
        if(collider.tag == "Enemy")
        {
            Debug.Log("Collision happened");
            mage.TakeDamage();

        }        
    }
}

Mage.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using Mirror;
using UnityEngine.Assertions;

public class Mage : NetworkBehaviour
{
    private Transform targetedEnemy;
    private bool enemyClicked = false;
    private bool walking;
    private Animator anim;
    private NavMeshAgent navAgent;
    private float nextShot;
    private float timeBetweenShots = 2f;
    private bool isAttacking = false;

    [SyncVar(hook="OnHealthChanged")] private int health = 100;
    private int fireDamage = 20;
    private Vector3 startingPosition;

    [SerializeField] private float shootDistance;
    [SerializeField] private Transform spawnPoint;
    [SerializeField] private GameObject fireballPrefab;
    [SerializeField] private GameObject playerHalo;
    [SerializeField] private TextMesh healthText;


    public override void OnStartLocalPlayer()
    {
        playerHalo.SetActive(true);
        tag = "Player";
    }

    // Start is called before the first frame update
    void Start()
    {
        anim = GetComponent<Animator>();
        navAgent = GetComponent<NavMeshAgent>();
        Assert.IsNotNull(spawnPoint);
        Assert.IsNotNull(fireballPrefab);
        Assert.IsNotNull(playerHalo);
        Assert.IsNotNull(healthText);
        startingPosition = transform.position;

    }

    // Update is called once per frame
    void Update()
    {      
        healthText.text = health.ToString(); //visible to all

        if(!isLocalPlayer)
        {
            return;
        }        

        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        if(Input.GetButtonDown("Fire2"))
        {
            if(Physics.Raycast(ray,out hit,100))
            {
                if(hit.collider.CompareTag("Enemy"))
                {
                    targetedEnemy = hit.transform;
                    enemyClicked = true;
                }
                else{
                    isAttacking = false;
                    enemyClicked = false;
                    walking = true;
                    navAgent.destination = hit.point;
                    navAgent.Resume();
                }
            }
        }

        if(enemyClicked)
        {
            MoveAndShoot();
        }

        if(navAgent.remainingDistance <= navAgent.stoppingDistance)
        {
            walking = false;
        }else{
            if(!isAttacking)
                walking = true;
        }

        anim.SetBool("IsWalking",walking);
    }

    private void MoveAndShoot()
    {
        if(targetedEnemy == null)
        {
            return;
        }

        navAgent.destination = targetedEnemy.position;

        if(navAgent.remainingDistance >= shootDistance)
        {
            navAgent.Resume();
            walking = true;
        }

        if(navAgent.remainingDistance <= shootDistance)
        {
            transform.LookAt(targetedEnemy);

            if(Time.time > nextShot)
            {
                isAttacking = true;
                nextShot = Time.time + timeBetweenShots;
                CmdFire();
            }

            navAgent.Stop();
            walking = false;
        }        
    }

    [Command]
    void CmdFire()
    {
        anim.SetTrigger("Attack");
        GameObject fireball = Instantiate(fireballPrefab,spawnPoint.position,spawnPoint.rotation) as GameObject;
        fireball.GetComponent<Rigidbody>().velocity = fireball.transform.forward*3;

        NetworkServer.Spawn(fireball);

        Destroy(fireball,2.5f);
    }

    void OnHealthChanged(int health,int updatedHealth)
    {
        healthText.text = updatedHealth.ToString();
    }

    public void TakeDamage()
    {
        Debug.Log("inside the function");
        
        if(!isServer)
        {
            return;
        }

        health -= fireDamage;

        if(health <= 0)
        {
            health = 100;
            RpcRespawn();
        }
    }

    [ClientRpc]
    void RpcRespawn()
    {
        if(isLocalPlayer)
        {
            transform.position = startingPosition;

        }
    }
}

Its working now. I didn't get the component of Mage in Fireball script, thats why it ws throwing error. Thanks guys.

This topic is closed to new replies.

Advertisement