0
\$\begingroup\$

In my 2d game in unity and c sharp, my friendly units are squares. Each square has a grid on it. Each cell of the grid can have one enemy attached to it at a time.

This means that my enemy's targets are not just the friendly game object, but the cell as well. Because of this, I've created a Target class for this (just contains a gameobject and cell). Enemies carry their Target reference so they know where to move to and who to attack.

However, my friendlies do not need the cell to target an enemy. Enemies are small and don't have grids on them. I cannot figure out a nice way to make this polymorphic(?) Target concept work without doing checks for "if cell is not null" in all of my targeting code.

For example, code in my behavior tree for checks like "HasTarget" are tightly coupled to check for existence of a gameobject and cell. It just feels wrong. Am I stuck using custom checks and code for an enemy vs friendly target?

I tried interfaces but they require the same return types. That differs depending on if they are an enemy or friendly. Something like unit.Itargettable.GetTarget() won't work.

Help!

\$\endgroup\$
6
  • \$\begingroup\$ Hi, I'm still having a little difficulty wrapping my head around the intended system (might be me). What would be the functions you want to have? If cell, square, enemy implement an interface ITarget, what goes wrong? ITarget could have GetTarget() with ITarget as return type, though friendlies could return enemy while enemy returns square or grid. (pardon me if I misunderstood the target relations.) The pool to pick a new target from, could be split into the invididual objects rather than ITarget (or you can make other solutions like a bool IsFriendly). \$\endgroup\$ – D.Kallan Feb 8 at 18:13
  • \$\begingroup\$ That's basically what I'm doing now, just not enforced by an interface yet. So I guess I CAN do that. But how do I nicely deal with getting a target position in the code that uses Target without an "if" to check of the Cell is null or not? Ideally, I don't want to make a GetTargetPosFriendly() and GetTargetPosEnemy() but I don't see any other way besides these two options. \$\endgroup\$ – Thisisstackoverflow Feb 8 at 18:46
  • \$\begingroup\$ But wouldn't your function be GetTargetPos() where cell.GetTargetPos() returns something different than enemy.GetTargetPos()? You could also turn it around, if a friendly or enemy has a target, you can say target.GetPosition() and based on whatever target is, it returns a different position. The Enemy class can reference a Friendly (unless they have to be able to attack eachother) while friendly can hold an Enemy. Unless there are some things I don't know about yet, it feels like abstraction (\w interface) would be fine for you. (Also, wouldn't the target.transform.position suffice?) \$\endgroup\$ – D.Kallan Feb 8 at 19:03
  • \$\begingroup\$ Well ideally I wanted to do target.GetPosition() and only reference a Unit, not a Friendly or Enemy. I wanted to keep it super abstract. I suppose I could do a target.GetPosition() that has some logic to check if cell is null and return the position of the gameobject instead. I waa hoping for this level of abstraction so that I can share AI nodes across Units. If I don't do this, I will need an EnemyAI and a FriendlyAI instead of just a UnitAI. Also, cells are not objects/monobehaviors. \$\endgroup\$ – Thisisstackoverflow Feb 8 at 19:53
  • \$\begingroup\$ Ah oke, well I think abstraction would be the way to go, and have different code in the implementing class, depending on the situation (cell check for example). Should you get stuck, you can probably update the question and add some code for people to help out. \$\endgroup\$ – D.Kallan Feb 9 at 6:38
1
\$\begingroup\$

I tried interfaces but they require the same return types. That differs depending on if they are an enemy or friendly. Something like unit.Itargettable.GetTarget() won't work.

It sounds like you haven't used interfaces correctly. If the return type needs to be the same type, then make the return type an interface. Then have both your friendly and enemy units implement that interface.

It's hard to understand exactly how your game is currently set up, because you haven't shared any of your code, but here's a rough example of how you might use an interface:

public ITargetableEvent : UnityEvent<ITargetable> {}

public interface ITargetable {
    string Name { get; }
    int HP { get; set; }
    Vector3 Position { get; set; }
    ITargetableEvent DeathEvent { get; }
}

public class Unit : ITargetable {
    [SerializeField] private string name;
    [SerializeField] private int hp = 100;
    [SerializeField] private int attackPower = 5;
    [SerializeField] private ITargetableEvent deathEvent = new ITargetableEvent();
    private ITargetable target;

    public string Name => name;
    public int HP {
        get => hp; 
        set {
            this.hp = value;
            if (this.hp <= 0) deathEvent.Invoke(this);
        }
    }

    virtual public Vector3 Position => transform.position;

    //our target can be anything that implements ITargetable
    public ITargetable Target { get => target; set => target = value; }

    public void Attack() {
        if (target != null) target.HP -= attackPower;
    }
}

//friendly unit position is determined by the cell?
public class FriendlyUnit : Unit {
    [SerializeField] private Cell cell;
    override public Vector3 Position => cell.transform.position;
}

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the help and trying to understand my poorly communicated question. What ended up solving the issue I had was making a GetTargetPos() function in Target that simply checked if a cell existed or not. You'll get the cell position if it exists, or the game objects position if not. I see your point above and can absolutely do such an interface for providing a Target to a requestor, initially. A friendly will hand over a Unit and Cell with Target, while an enemy will just hand over a Unit. \$\endgroup\$ – Thisisstackoverflow Feb 10 at 8:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.