将分配给另一个对象变量的对象值更改

时间:2019-04-25 04:34:10

标签: c# unity3d

基本上我想做的是使用存储的默认值重置玩家统计信息。问题是当玩家恢复活力并恢复其统计信息时,当我没有更改其值时,默认统计信息最终就会更改。

我尝试使用复制构造函数并分别设置每个变量,并且它可以工作。直接设置它的某些方法最终导致了该错误。

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

/*
 *      things to do:
 *      1. respawn point
 *      2. fix this strange bug
 */
namespace Outbreak
{
    public class LivingEntity : MonoBehaviour, IDamageable
    {
        public Stats defaultStats;
        public event System.Action OnDeath;


        protected Stats stats;
        public Stats Stats
        {
            get
            {
                return stats;
            }
        }

        protected virtual void Awake()
        {
            stats = new Stats(defaultStats);
        }

        // Start is called before the first frame update
        protected virtual void Start()
        {

        }

        // Update is called once per frame
        protected virtual void Update()
        {

        }

        public void TakeDamage(float damage)
        {
            Debug.Log(defaultStats.IsDead);

            //if not dead
            if (stats.IsDead == false)
            {
                //and it still has health left
                if (stats.Health > 0)
                {
                    //take damage
                    stats.Health -= damage; 
                }
                //after taking damage check if health is depleted.
                if (stats.Health <= 0)
                {
                    //pronouce it dead
                    Die();
                }
            }

        }

        protected virtual void Die()
        {
            //set its status to dead
            stats.IsDead = true;

            //broadcast to all listener that this player is dead
            if (OnDeath != null)
            {
                OnDeath();
            }

            //make player invisible
            gameObject.GetComponent<MeshRenderer>().enabled = false;
            //prevent any collision
            gameObject.GetComponent<CapsuleCollider>().enabled = false;
            //prevent player detecting collision
            gameObject.GetComponent<Rigidbody>().detectCollisions = false;
            //set to kinematic
            gameObject.GetComponent<Rigidbody>().isKinematic = true;
        }

        protected IEnumerator DelayedRevival()
        {
            yield return new WaitForSeconds(3.0f);
            Revive();
            yield return null;
        }

        protected virtual void Revive()
        {
            //2.  reset to default stats
            //stats = new Stats(defaultStats);
            //stats.IsDead = false;
            //stats.Health = 3;
            //stats.MovementSpeed = 10;
            stats = defaultStats;

            //1. set position to last respawn point location
            transform.position = Vector3.zero + (Vector3.up * 1.5f);

            //make player visible
            gameObject.GetComponent<MeshRenderer>().enabled = true;
            //allow for collision
            gameObject.GetComponent<CapsuleCollider>().enabled = true;
            //allow player to detect collision
            gameObject.GetComponent<Rigidbody>().detectCollisions = true;
            //set to dynamic
            gameObject.GetComponent<Rigidbody>().isKinematic = false;
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Outbreak
{
    [RequireComponent(typeof(CharacterController), typeof(CharacterInput))]
    public class Character : LivingEntity
    {

        protected override void Awake()
        {
            base.Awake();

        }
        // Start is called before the first frame update
        protected override void Start()
        {
            base.Start();
        }

        // Update is called once per frame
        protected override void Update()
        {
            base.Update();
        }

        protected override void Die()
        {
            base.Die();
            gameObject.GetComponent<CharacterInput>().enabled = false;
            gameObject.GetComponent<CharacterController>().enabled = false;
            StartCoroutine(DelayedRevival());
        }

        protected override void Revive()
        {
            base.Revive();
            gameObject.GetComponent<CharacterInput>().enabled = true;
            gameObject.GetComponent<CharacterController>().enabled = true;
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Outbreak
{
    [System.Serializable]
    public class Stats
    {
        [SerializeField]
        private float health;
        [SerializeField]
        private float movementSpeed;
        [SerializeField]
        private bool isDead;

        public Stats()
        {
            health = 3.0f;
            movementSpeed = 5.0f;
            isDead = false;
        }

        public Stats(Stats stats)
        {
            health = stats.health;
            movementSpeed = stats.movementSpeed;
            isDead = stats.isDead;
        }

        public float Health
        {
            set
            {
                health = value;
            }
            get
            {
                return health;
            }
        }

        public float MovementSpeed
        {
            set
            {
                movementSpeed = value;
            }
            get
            {
                return movementSpeed;
            }
        }

        public bool IsDead
        {
            set
            {
                isDead = value;
            }
            get
            {
                return isDead;
            }
        }
    }
}

我希望不要更改默认值,但最终结果会更改默认值。

1 个答案:

答案 0 :(得分:1)

您的问题专线是

stats = defaultStats;

由于类型Stats引用类型而不是值类型,因此该分配使stats与{{ 1}} =>它们现在指向一个相同的对象。

您将来对其中之一所做的任何更改都会对一个相同的引用defaultStats对象进行。


您应该像在Stats中那样进行作业:

Awake

将复制值。

或者,也可以使 stats = new Stats(defaultStats); 而不是Stats而是class,它将其转换为值类型

struct

提示:如评论中所述,您应该在所有[Serializable] public struct Stats { ... } 电话中仅进行一次呼叫,例如GetComponent,并在以后重新使用商店引用,例如

Awake

稍后,您将重用这些存储的引用,例如

private MeshRenderer meshRenderer;
private CapsuleCollider capsuleCollider;
private Rigidbody rigidBody;

private void Awake()
{
    meshRenderer = GetComponent<MeshRenderer>();
    capsuleCollider = GetComponent<CapsuleCollider>();
    rigidBody = GetComponent<RigidBody>();
}

meshRenderer.enabled = false; capsuleCollider.enabled = false; rigidBody.detectCollisions = false; rigidBody.isKinematic = true; 中,您对CharacterCharacterInput进行相同操作。