Unity继承创建成员变量的副本

时间:2019-09-09 23:18:38

标签: c# unity3d inheritance

我正在尝试通过使用继承来减少项目中重复代码的数量。

例如:

我游戏中的每个角色,无论是玩家,敌人还是NPC,都拥有HP成员字段。

它们还具有TakeDamage功能,可以影响HP。

在我的游戏中,此功能非常详尽地比较了攻击者的统计信息,攻击武器的统计数据与被攻击者的统计数据及其装备。

此功能经常调整以获得它,这样感觉就对了,我不希望它在npc,敌人和玩家控制器上重复出现,而让我们也只想象一个“ networkPlayer”,因为嘿,一个人可以梦想!

还有很多类似的功能,我真的很想把它们全部放在一个PlayerController,EnemyController等继承的CharacterController类中。

好吧,这是继承的正确用法吗?

这里也有第二层。

我的玩家,敌人和NPC都是通过在线数据库构建的,该数据库随着添加新的敌人和玩家而变化。

所以我有一个CharacterBase类,该类在HP,Stats,Name等字符之间构建共享变量。

然后有诸如EnemyBase之类的派生类(继承名称,hp,来自Character基数的统计信息),但在该类上扩展了自己的变量,例如Rarety,MaxGoldHeMayCarry,XPGainedIfYouKillMe等...

现在的问题是:

EnemyBase扩展了CharacterBase 有两份惠普! 在检查器中,它显示为CharacterBase-> HP和EnemyBase-> HP

当我打电话时 CharacterController.HP减一 从CharcterBase HP减去1 但是,这是没有动过的EnemyBase HP!

这真的很罗word。对不起。以下是一些测试代码来演示

public class TestingInheritance : MonoBehaviour
{
    // set this to an empty prefab in the inspector
    public GameObject TestObjectPrefab;

    private void Start()
    {
        GameObject TestPlayer = Instantiate(TestObjectPrefab);

        PlayerController pController = TestPlayer.AddComponent<PlayerController>();
        pController.TakeDamage();   //calls the overridden version of TakeDamage() (which is good)


        GameObject TestEnemy = Instantiate(TestObjectPrefab);

        EnemyController eController = TestEnemy.AddComponent<EnemyController>();
        eController.TakeDamage();   //calls the overrideden version in EnemyController (which is good)

    }
}

[System.Serializable]   //comes from db
public class CharacterBase
{
    public int HP=100;
}

[System.Serializable]   //comes from db
public class Enemy : CharacterBase
{
    public string Name = "Enemy";
}

[System.Serializable]   //comes from db
public class Player : CharacterBase
{
    public string Name = "Player";
}

public class CharacterController : MonoBehaviour
{

    //I want to be able to access the variables that all characters share in common, such as HP, Name, Stats, Inventory
    public CharacterBase characterBase = new CharacterBase();

    /// <summary>
    /// Reduces HP by 1
    /// TakeDamage() in my real game is actually much more complicated and I dont want to duplicate the code in Enemy, Player, and NPC 
    /// there are many more functions that I want to be able to call for any kind of character such as UseItem(potion), ChangeState(poisoned), Die()
    /// All these functions share a ton of the same code and I dont want to duplicate it, especially because it changes often during my development
    /// </summary>
    public virtual void TakeDamage()
    {
        //here we just loose one hp
        characterBase.HP -= 1;
    }

}

public class PlayerController : CharacterController
{
    public Player playerBase = new Player();

    public override void TakeDamage()
    {
        //do the code that is shared for all Characters
        base.TakeDamage();

        Debug.Log(playerBase.Name + " was hit!--- HP now: " + playerBase.HP);
        //Prints Player was hit! --- HP now: 100 and the inspector shows CharacterBase >> HP 99 AND PlayerBase >> HP: 100, Name: Player

        //IF WE CHANGE THE ABOVE LINE TO SAY
        Debug.Log(playerBase.Name + " was hit!--- HP now: " + characterBase.HP); 
        //Now we print 99, which is good. but we still have both CharacterBase >> HP 99
        //AND the Wrong, ugly, bad, confusing PlayerBase >> HP: 100

    }
}


public class EnemyController : CharacterController
{
    public Enemy enemyBase = new Enemy();

    public override void TakeDamage()
    {
        base.TakeDamage();
        Debug.Log(enemyBase.Name + " was hit! --- HP now: " + enemyBase.HP);
        //Prints Enemy was hit! --- HP now: 100 and the inspector shows CharacterBase >> HP 99 AND EnemyBase >> HP: 100, Name: Enemy
    }
}

what the inpsector show

那么,我该如何重组它,使其只有一个变量HP可供CharacterController和PlayerController访问?

很抱歉,这么长时间了,有人还在读书吗?我不知道如何使它变得更加简洁...

2 个答案:

答案 0 :(得分:4)

为什么在这里?

public CharacterBase characterBase = new CharacterBase();

所有继承自它的类都将与它们的自己代码(例如public Enemy enemyBase = new Enemy();)分开调用。

这就是为什么您同时看到两者。结果,每个控制器都有两个单独的统计信息。如果要访问它,则需要在字符控制器中创建一个get方法:

public CharacterBase stats { get; protected set; }

然后在其子类中用值填充该字段。

答案 1 :(得分:0)

感谢Draco18的回答使它奏效了。

为了避免重复的字段,我们绝对需要删除:

public CharacterBase characterBase = new CharacterBase();

但是我们仍然需要对characterBase的引用,所以我添加了:

    protected CharacterBase characterBase;
    public void Init(CharacterBase _characterBase)
    {
        characterBase = _characterBase;
    }

现在Start()看起来像这样

private void Start()
    {
        GameObject TestPlayer = Instantiate(TestObjectPrefab);
        TestPlayer.name = "p";

        PlayerController pController = TestPlayer.AddComponent<PlayerController>();
        pController.Init(pController.playerBase);
        pController.TakeDamage();  
    }

做到了!没有更多重复的字段!并在Debug.Log中正确输出-谢谢大家!