0

Say I am using a game engine that provides the following. More importantly, the HitResult is the only thing provided to me when I ray trace from a Player. Thus I cast the HitActor to see if I hit a player or not. Is there a way around using a dynamic cast in this situation without changing any Engine code? Thanks! Edit: I realize I should be using smart pointers and some bad formatting ;)

#include <bits/stdc++.h>
#include <iostream>

using namespace std;

/** Engine Code Start */
class Actor
{
public:
    Actor() = default;
    Actor(string name) : m_name{name} {};
    virtual ~Actor() = default;

    string GetName() { return m_name; }
    void SetName(string name) { m_name = name; }

private:
    string m_name{""};
};

class Person : public Actor
{
public:
    Person() = default;
    Person(string name) : Actor{name} {};
    virtual ~Person() = default;

};

class Wall : public Actor
{
public:
    Wall() = default;
    Wall(string name, uint32_t height) : Actor{name}, m_height{height} {}; 
    virtual ~Wall() = default;

    uint32_t GetHeight() { return m_height; }
    void SetHeight(uint32_t height) { m_height = height; }

private:
    uint32_t m_height{0};
};

struct HitResult
{
    HitResult() = default;
    HitResult(Actor* actor, uint8_t damage) : m_hitactor{actor}, m_damage{damage} {};
    virtual ~HitResult() = default;

    Actor* m_hitactor = nullptr;
    uint8_t m_damage;

    Actor* GetHitActor() { return m_hitactor; }
    uint8_t GetHitDamage() { return m_damage; }
};

/** Engine Code End */

// Determine if actor hit is Person or something else?
int main(){      
    Person* player1 = new Person("Bob");
    Person* player2 = new Person("Phil");
    Wall* wall1 = new Wall("Wall1", 15);

    if (player1 == nullptr || player2 == nullptr || wall1 == nullptr)
    {
        delete player1;
        delete player2;
        delete wall1;
        return -1;
    }

    cout << "Player1's Name: " << player1->GetName() << endl;
    cout << "Player2's Name: " << player2->GetName() << endl;
    cout << "Wall1's Name: " << wall1->GetName() << ", height = " << wall1->GetHeight() << endl;

    // Player1 Shoots at Player2, determine if object shot is Wall or Player object:
    // Caveat: Engine only provides Base Class (Person) _not_ a Derived Class (Actor)!
    HitResult hr(player2, 50);

    // Question: Is there a way to _not_ use a dynamic cast here?
    Person* HitPlayer = dynamic_cast<Person*>(hr.GetHitActor());
    if (HitPlayer != nullptr)
    {
        cout << "Player hit!" << endl;
    }
    else
    {
        cout << "Person not hit!" << endl;
    }

    delete player1;
    delete player2;
    delete wall1;

    return 0;
}

The live example is here: Live Example

Edit: Main Question: Given the HitResult struct as input is there a way, besides dynamic_cast, to check if the hit actor is a Person object and not a Wall object?

Standard Out:

Player1's Name: Bob
Player2's Name: Phil
Wall1's Name: Wall1, height = 15
Player hit!
mpro34
  • 96
  • 6
  • 1
    Why you don't want cast? – apple apple Sep 30 '21 at 19:21
  • 2
    This has nothing to do with the question, but I strongly using smart pointers for the new/delete logic. Furthermore you'll likely get a `std::bad_alloc` exception instead of having the `new` operator yield `nullptr`. `auto player1 = std::make_unique("Bob");` Creates bob and makes sure bob gets deleted, whether you exit the function via return or exception, no `delete` operation required. – fabian Sep 30 '21 at 19:21
  • `if (player1 == nullptr || player2 == nullptr || wall1 == nullptr)` -- The compiler optimizer may remove that entire block, including the `return -1`, since `new` never returns `nullptr` unless `nothrow` is specified. – PaulMcKenzie Sep 30 '21 at 19:25
  • 1
    Given the "without changing the engine code" constraint, you're stuck using `dynamic_cast`. The "immutable" engine code as given does not facilitate your goal. – Eljay Sep 30 '21 at 19:25
  • 2
    Unrelated: The presence of both `#include ` and `#include ` suggests that you don't really understand what `#include ` does and how to use it correctly. Here is a bit of reading that may save you from some future woes: [Why should I not #include ?](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) – user4581301 Sep 30 '21 at 19:32
  • thanks for the comments, updated the question with some clarity – mpro34 Sep 30 '21 at 23:52
  • The alternative would be to provide your own RTTI. That might be e.g. an `enum` with enumerators for each of your class (derived from `Actor`), and a virtual method in `Actor` which is overridden in each class to return the correct resp. enumerator. This provides the advance that you can use `switch` to distinguish handling of derived `Actor`s but it still requires modification of your game engine if `Actor` is part of it. Another alternative is to use a `std::map` which maps std:: RTTI type index to functors (e.g. lambdas). - I'm not sure whether this is really that better... – Scheff's Cat Oct 01 '21 at 06:37

0 Answers0