3D Pong: The Opponent

The only object missing from our game of Pong now is the opponent paddle. Adding it is easy as there is no new engine features involved.

Rendering the Opponent

Copy PaddleRight.csmodel from the 3D Pong Asset Pack and build it using the content build pipeline. Render the opponent in State.h by adding the following to State::OnInit():

auto opponentModel = resourcePool->LoadResource(CSCore::StorageLocation::k_package, "Models/PaddleRight.csmodel");
        
CSRendering::StaticMeshComponentSPtr opponentModelComponent = renderComponentFactory->CreateStaticMeshComponent(playerModel, modelsMaterial);
        
CSCore::EntitySPtr opponentEntity = CSCore::Entity::Create();
opponentEntity->AddComponent(opponentModelComponent);
        
GetScene()->Add(opponentEntity);
        
opponentEntity->GetTransform().SetPosition(CSCore::Vector3(50.0f, 0.0f, 0.0f));

The Opponent Component

We also need a new component to make the opponent follow the ball. Some simple follow logic can be added to OnUpdate(). Since we’ll need a reference to the ball entity, it should be passed in to the constructor. Add the following files:

OpponentMovementComponent.h:

#ifndef _PONG_OPPONENTMOVEMENTCOMPONENT_H_
#define _PONG_OPPONENTMOVEMENTCOMPONENT_H_

#include 

#include 

namespace Pong
{
    class OpponentMovementComponent final : public CSCore::Component
    {
    public:
        CS_DECLARE_NAMEDTYPE(OpponentMovementComponent);
        OpponentMovementComponent(CSCore::Entity* in_ballEntity);
        bool IsA(CSCore::InterfaceIDType in_interfaceId) const override;
        
    private:
        void OnUpdate(f32 in_deltaTime) override;
        
        CSCore::Entity* m_ballEntity = nullptr;
    };
}

#endif

OpponentMovementComponent.cpp:

#include 

namespace Pong
{
    CS_DEFINE_NAMEDTYPE(OpponentMovementComponent);
    
    OpponentMovementComponent::OpponentMovementComponent(CSCore::Entity* in_ballEntity)
        : m_ballEntity(in_ballEntity)
    {
        CS_ASSERT(m_ballEntity != nullptr, "Must supply the ball entity");
    }
    
    bool OpponentMovementComponent::IsA(CSCore::InterfaceIDType in_interfaceId) const
    {
        return (OpponentMovementComponent::InterfaceID == in_interfaceId);
    }
    
    void OpponentMovementComponent::OnUpdate(f32 in_deltaTime)
    {
        auto& transform = GetEntity()->GetTransform();
        
        f32 distance = m_ballEntity->GetTransform().GetWorldPosition().y - transform.GetWorldPosition().y;
        transform.MoveBy(0.0f, distance * 0.14f, 0.0f);
        
        const f32 k_limits = 30.0f;
        if (transform.GetWorldPosition().y > k_limits)
        {
            transform.SetPosition(transform.GetWorldPosition().x, k_limits, transform.GetWorldPosition().z);
        }
        if (transform.GetWorldPosition().y < -k_limits)
        {
            transform.SetPosition(transform.GetWorldPosition().x, -k_limits, transform.GetWorldPosition().z);
        }
    }
}

Remember and add the new class to ForwardDeclarations.h:

CS_FORWARDDECLARE_CLASS(OpponentMovementComponent);

Now include OpponentMovementComponent.h in State.cpp and add this to State::OnInit():

auto opponentMovementComponent = std::make_shared(ballEntity.get());
opponentEntity->AddComponent(opponentMovementComponent);

Build and run this and you should see the opponent paddle chasing the ball.

Pong-Opponent

Next: Collisions