c# - 分配的对象值在分配给另一个对象变量时发生了变化
问题描述
基本上我想要做的是使用存储的默认值重置玩家统计数据。问题是当玩家复活并恢复其统计数据时,当我没有更改其值时,默认统计数据最终会发生变化。
我尝试使用复制构造函数并单独设置每个变量并且它可以工作。一些如何直接设置它最终导致了那个错误。
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;
}
}
}
}
我希望默认值不应该改变,但实际结果最终会随着默认值的改变而改变。
解决方案
你的问题是
stats = defaultStats;
由于该类型Stats
是引用类型而不是值类型,因此该赋值stats
具有与 => 相同的引用,defaultStats
它们现在指向同一个对象。
您对其中之一所做的任何未来更改都将针对同一个引用Stats
对象进行。
您应该像在以下情况下那样进行分配Awake
:
stats = new Stats(defaultStats);
而是复制值。
或者,您可以将您的Stats
not a class
but 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
你做同样的事情CharacterInput
and CharacterController
。
推荐阅读
- java - 如何使用 docker ENTRYPOINT 执行 java,然后在 java 进程完成后执行 touch
- c++ - QGraphicsTextItem 没有方法: setDefaultTextColor 和 setPos
- flutter - 从切换按钮获取字符串值
- linux - 如何在终端中显示所有使用一个命令的端口的进程?
- r - 如何加快 R 中 pgmm 模型的运行速度?
- r - 带有 tsmp R 包的自定义注释向量
- java - 通过服务器处理数据包导致 CPU 大幅增加
- macos - AnzoGraph docker 支持 Mac m1
- blazor - 如何通过单击 blazor 中的按钮将数据从 foreach 循环传递到另一个页面
- r - 替换字符串中的重复字符