首页 > 解决方案 > 分配的对象值在分配给另一个对象变量时发生了变化

问题描述

基本上我想要做的是使用存储的默认值重置玩家统计数据。问题是当玩家复活并恢复其统计数据时,当我没有更改其值时,默认统计数据最终会发生变化。

我尝试使用复制构造函数并单独设置每个变量并且它可以工作。一些如何直接设置它最终导致了那个错误。

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;
            }
        }
    }
}

我希望默认值不应该改变,但实际结果最终会随着默认值的改变而改变。

标签: c#unity3d

解决方案


你的问题是

stats = defaultStats;

由于该类型Stats引用类型而不是值类型,因此该赋值stats具有与 => 相同的引用defaultStats它们现在指向同一个对象。

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


您应该像在以下情况下那样进行分配Awake

 stats = new Stats(defaultStats);

而是复制值。

或者,您可以将您的Statsnot a classbut astruct转换为 value-type

[Serializable]
public struct Stats
{
    ...
}

提示:正如评论中所说,您应该GetComponent只在 eg 中进行一次所有调用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;

Character你做同样的事情CharacterInputand CharacterController


推荐阅读