Enemy interactions

Sooo long without an update. My excuses include: laziness, being so close to releasing a game in my day job leaves no mental energy for home projects, travel, and working with a mentor outside of work hours to better understand data oriented development.

I have slowly been refactoring the Enemy class. Not long into my first attempt I saw how similar the PlayerObject and EnemyObject were going to be, so I created a ShipObject to stop myself from repeating code. I moved the component updating and shoot code into the ShipObject which leaves the Player update all about input handling:

GLFWwindow* currentContext = glfwGetCurrentContext();
PhysicsComponent* physicsComponent = dynamic_cast<PhysicsComponent*>(GameObject::GetComponent(PHYSICS));
if (physicsComponent != nullptr) {
    if (glfwGetKey(currentContext, 'W')) {
        physicsComponent->AddForce(glm::vec2(150, 150));
    }

    if (glfwGetKey(currentContext, 'A')) {
        physicsComponent->AddRotation(3);
    }

    if (glfwGetKey(currentContext, 'D')) {
        physicsComponent->AddRotation(-3);
    }
    if (glfwGetKey(currentContext, GLFW_KEY_SPACE)) {
        shoot();
    }
}

ShipObject::Update(a_dDeltaTime);

So now for the Enemy. With all the rest of the clutter gone all I have left is the “AI” of the enemy following the player and shooting when within range. So the problem here is that the Enemy needs to know the position of the Player. In the old code I just passed the player’s vec3 position into the Enemy’s update function as a constant. However with my new GameObject approach I couldn’t really justify modifying my EnemyObject to use different Update parameters.

I briefly toyed with the idea of making the Enemy listen for changes to the Player’s position with some sort of Observer pattern. Despite wanting to practice that pattern a little, it seemed like a huge amount of overkill. So for now I’ve created a GetPosition function in PlayerObject, and the EnemyObject now contains a pointer to the PlayerObject in order to use this function when necessary.

I still don’t really like this since the EnemyObject now has full read/write access to the PlayerObject’s position (hmm, why am I even passing back the actual position in GetPosition? Why don’t I make a copy to pass back? Still, even with this improvement the Enemy has access to all the PlayerObject’s public functions – like adding components).

With that decision out of the way all that was left was adding in the “AI”. It remains basically unchanged from the original code – just modified to work with the new component system:

PhysicsComponent* physicsComponent = dynamic_cast<PhysicsComponent*>(GameObject::GetComponent(PHYSICS));
glm::vec3 playerPos = m_player->GetPosition();

glm::vec3 toPlayer = playerPos - physicsComponent->m_position;

float aimAngle = atan2(toPlayer.y, toPlayer.x);

if (aimAngle*180.0 / 3.14159 > physicsComponent->m_fRotationAngle + 90)
    physicsComponent->AddRotation(3);
else if (aimAngle*180.0 / 3.14159 < physicsComponent->m_fRotationAngle + 90)
    physicsComponent->AddRotation(-3);

if (physicsComponent->m_fRotationAngle + 90 > 180)
    physicsComponent->AddRotation(-360);
else if (physicsComponent->m_fRotationAngle + 90 < -180)
    physicsComponent->AddRotation(360);

physicsComponent->AddForce(glm::vec2(50, 50));

if (glm::distance(playerPos, physicsComponent->m_position) < 200) {
    ShipObject::shoot();
}

ShipObject::Update(a_dDeltaTime);

One thing I’m contemplating is creating a component similar to Unity’s “transform” component. This can store the position, rotation + scale. And then each other component should have access to the transform. This way I can remove all the code that looks like this:

SpriteComponent* spriteComponent = dynamic_cast<SpriteComponent*>(GameObject::GetComponent(SPRITE));
PhysicsComponent* physicsComponent = dynamic_cast<PhysicsComponent*>(GameObject::GetComponent(PHYSICS));
if (spriteComponent != nullptr) {
    if (physicsComponent != nullptr) {
        //Is there a better way to get these components to work together? I feel it's likely           to happen a lot.
        spriteComponent->m_position = physicsComponent->m_position;
        spriteComponent->m_fRotationAngle = physicsComponent->m_fRotationAngle;
    }
}

So, next step (unless there’s anyone who thinks this is a bad idea) is to create a transformComponent. Then perhaps I can finally get to collisions!

Thanks for all the comments from last time – they definitely give me a lot to think about and I think I will implement some of the suggestions soon.

Once again, all my code is on github if you’d like to read it without the ugly WordPress formatting.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s