탑다운 미니맵 구현
This commit is contained in:
@@ -25,6 +25,13 @@
|
||||
|
||||
#include "MainScene.h"
|
||||
|
||||
#if defined(AX_ENABLE_3D_PHYSICS)
|
||||
# include "physics3d/Physics3DWorld.h"
|
||||
#endif
|
||||
#if defined(AX_ENABLE_NAVMESH)
|
||||
# include "navmesh/NavMesh.h"
|
||||
#endif
|
||||
|
||||
using namespace ax;
|
||||
|
||||
static int s_sceneID = 1000;
|
||||
@@ -68,7 +75,8 @@ bool MainScene::init()
|
||||
this->addChild(menu, 1);
|
||||
|
||||
/////////////////////////////
|
||||
// 3. 3D Setup
|
||||
// 3D Setup
|
||||
const unsigned short cameraMask3D = (unsigned short)CameraFlag::USER1 | (unsigned short)CameraFlag::USER2;
|
||||
|
||||
// Grid settings
|
||||
const int gridSize = 10;
|
||||
@@ -82,19 +90,19 @@ bool MainScene::init()
|
||||
Vec3 gridCenter(gridWidth / 2.0f, 0.0f, gridWidth / 2.0f);
|
||||
|
||||
// Create a single cube at the center of the grid area
|
||||
auto centerCube = MeshRenderer::create("cube.obj");
|
||||
if (centerCube)
|
||||
_playerCube = MeshRenderer::create("cube.obj");
|
||||
if (_playerCube)
|
||||
{
|
||||
centerCube->setPosition3D(gridCenter);
|
||||
centerCube->setCameraMask((unsigned short)CameraFlag::USER1);
|
||||
this->addChild(centerCube);
|
||||
_playerCube->setPosition3D(gridCenter);
|
||||
_playerCube->setCameraMask(cameraMask3D);
|
||||
this->addChild(_playerCube);
|
||||
}
|
||||
|
||||
// Add Lights
|
||||
// Directional Light: Like sunlight, creating shadows/depth
|
||||
Vec3 lightDir(-1.0f, -1.0f, -0.5f);
|
||||
auto dirLight = DirectionLight::create(lightDir, Color3B::WHITE);
|
||||
dirLight->setCameraMask((unsigned short)CameraFlag::USER1);
|
||||
dirLight->setCameraMask(cameraMask3D);
|
||||
this->addChild(dirLight);
|
||||
|
||||
// Visual marker for the Directional Light source
|
||||
@@ -109,13 +117,13 @@ bool MainScene::init()
|
||||
lightMarker->setPosition3D(markerPos);
|
||||
lightMarker->setScale(0.5f);
|
||||
lightMarker->setColor(Color3B::YELLOW);
|
||||
lightMarker->setCameraMask((unsigned short)CameraFlag::USER1);
|
||||
lightMarker->setCameraMask(cameraMask3D);
|
||||
this->addChild(lightMarker);
|
||||
}
|
||||
|
||||
// Ambient Light: To ensure dark sides are still somewhat visible
|
||||
auto ambLight = AmbientLight::create(Color3B(80, 80, 80));
|
||||
ambLight->setCameraMask((unsigned short)CameraFlag::USER1);
|
||||
ambLight->setCameraMask(cameraMask3D);
|
||||
this->addChild(ambLight);
|
||||
|
||||
// Add a Ground Plane
|
||||
@@ -132,14 +140,15 @@ bool MainScene::init()
|
||||
ground->setScaleY(0.25f); // Thickness set to 0.25
|
||||
// Positioned so the top surface is at Y = -0.5 (cubes are at Y=0 with height 1)
|
||||
ground->setPosition3D(Vec3(gridCenter.x, -0.5f - 0.125f, gridCenter.z));
|
||||
ground->setCameraMask((unsigned short)CameraFlag::USER1);
|
||||
ground->setCameraMask(cameraMask3D);
|
||||
ground->setColor(Color3B(100, 100, 100));
|
||||
this->addChild(ground);
|
||||
}
|
||||
|
||||
// Setup Camera for Quarter View (Isometric-like)
|
||||
// Setup Main Camera for Quarter View (Isometric-like)
|
||||
_camera3D = Camera::createPerspective(60.0f, visibleSize.width / visibleSize.height, 0.1f, 1000.0f);
|
||||
_camera3D->setCameraFlag(CameraFlag::USER1);
|
||||
_camera3D->setDepth(1);
|
||||
|
||||
// Initial camera position setup
|
||||
_targetPos = gridCenter;
|
||||
@@ -150,6 +159,36 @@ bool MainScene::init()
|
||||
|
||||
this->addChild(_camera3D);
|
||||
|
||||
// Setup Mini-map Camera for Top-down View
|
||||
float miniMapSize = 200.0f;
|
||||
_miniMapCamera = Camera::createPerspective(60.0f, 1.0f, 0.1f, 1000.0f);
|
||||
_miniMapCamera->setCameraFlag(CameraFlag::USER2);
|
||||
_miniMapCamera->setDepth(2);
|
||||
|
||||
_miniMapCamera->setPosition3D(gridCenter + Vec3(0, 30.0f, 0.01f)); // Offset slightly on Z to avoid gimbal lock with up vector
|
||||
_miniMapCamera->lookAt(gridCenter, Vec3(0, 0, -1));
|
||||
this->addChild(_miniMapCamera);
|
||||
|
||||
// Mini-map Border (UI)
|
||||
auto miniMapBorder = DrawNode::create();
|
||||
if (miniMapBorder)
|
||||
{
|
||||
Vec2 pos(visibleSize.width - miniMapSize - 20, visibleSize.height - miniMapSize - 20);
|
||||
|
||||
// Draw the border line
|
||||
miniMapBorder->drawRect(pos, pos + Vec2(miniMapSize, miniMapSize), Color4F::WHITE);
|
||||
// Add a semi-transparent dark background for the map area
|
||||
miniMapBorder->drawSolidRect(pos, pos + Vec2(miniMapSize, miniMapSize), Color4F(0, 0, 0, 0.4f));
|
||||
|
||||
// This belongs to the UI (DEFAULT camera)
|
||||
miniMapBorder->setCameraMask((unsigned short)CameraFlag::DEFAULT);
|
||||
this->addChild(miniMapBorder, 10);
|
||||
_miniMapBorder = miniMapBorder;
|
||||
}
|
||||
|
||||
// Ensure Default Camera (UI) has higher depth to be on top
|
||||
this->getDefaultCamera()->setDepth(3);
|
||||
|
||||
// Mouse Listener
|
||||
_mouseListener = EventListenerMouse::create();
|
||||
_mouseListener->onMouseMove = AX_CALLBACK_1(MainScene::onMouseMove, this);
|
||||
@@ -158,6 +197,12 @@ bool MainScene::init()
|
||||
_mouseListener->onMouseScroll = AX_CALLBACK_1(MainScene::onMouseScroll, this);
|
||||
_eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this);
|
||||
|
||||
// Keyboard Listener
|
||||
_keyboardListener = EventListenerKeyboard::create();
|
||||
_keyboardListener->onKeyPressed = AX_CALLBACK_2(MainScene::onKeyPressed, this);
|
||||
_keyboardListener->onKeyReleased = AX_CALLBACK_2(MainScene::onKeyReleased, this);
|
||||
_eventDispatcher->addEventListenerWithSceneGraphPriority(_keyboardListener, this);
|
||||
|
||||
// Disable the default 2D camera for these 3D objects by using CameraFlag::USER1
|
||||
// The label and menu will use the default camera (CameraFlag::DEFAULT)
|
||||
|
||||
@@ -174,6 +219,95 @@ bool MainScene::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper to access protected member of Camera
|
||||
namespace ax
|
||||
{
|
||||
class CameraHacker : public Camera
|
||||
{
|
||||
public:
|
||||
static void setVisitingCamera(Camera* cam) { _visitingCamera = cam; }
|
||||
};
|
||||
}
|
||||
|
||||
void MainScene::render(Renderer* renderer, const Mat4& eyeTransform, const Mat4* eyeProjection)
|
||||
{
|
||||
auto visibleSize = _director->getVisibleSize();
|
||||
const auto& transform = getNodeToParentTransform();
|
||||
|
||||
for (const auto& camera : getCameras())
|
||||
{
|
||||
if (!camera->isVisible())
|
||||
continue;
|
||||
|
||||
ax::CameraHacker::setVisitingCamera(camera);
|
||||
|
||||
// Set Viewport for this camera
|
||||
if (camera == _miniMapCamera)
|
||||
{
|
||||
float miniMapSize = 200.0f;
|
||||
Camera::setDefaultViewport(Viewport().set(
|
||||
(int)(visibleSize.width - miniMapSize - 20),
|
||||
(int)(visibleSize.height - miniMapSize - 20),
|
||||
(int)miniMapSize,
|
||||
(int)miniMapSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
Camera::setDefaultViewport(Viewport().set(0, 0, (int)visibleSize.width, (int)visibleSize.height));
|
||||
}
|
||||
|
||||
if (eyeProjection)
|
||||
camera->setAdditionalProjection(*eyeProjection * camera->getProjectionMatrix().getInversed());
|
||||
|
||||
camera->setAdditionalTransform(eyeTransform.getInversed());
|
||||
_director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
|
||||
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, camera->getViewProjectionMatrix());
|
||||
|
||||
camera->apply();
|
||||
// clear background with max depth
|
||||
camera->clearBackground();
|
||||
// visit the scene
|
||||
visit(renderer, transform, 0);
|
||||
|
||||
#if defined(AX_ENABLE_NAVMESH)
|
||||
if (_navMesh && _navMeshDebugCamera == camera)
|
||||
{
|
||||
_navMesh->debugDraw(renderer);
|
||||
}
|
||||
#endif
|
||||
|
||||
renderer->render();
|
||||
|
||||
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
|
||||
}
|
||||
|
||||
#if defined(AX_ENABLE_3D_PHYSICS)
|
||||
if (_physics3DWorld && _physics3DWorld->isDebugDrawEnabled())
|
||||
{
|
||||
Camera* physics3dDebugCamera = _physics3dDebugCamera != nullptr ? _physics3dDebugCamera : this->getDefaultCamera();
|
||||
|
||||
if (eyeProjection)
|
||||
physics3dDebugCamera->setAdditionalProjection(*eyeProjection *
|
||||
physics3dDebugCamera->getProjectionMatrix().getInversed());
|
||||
|
||||
physics3dDebugCamera->setAdditionalTransform(eyeTransform.getInversed());
|
||||
_director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
|
||||
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,
|
||||
physics3dDebugCamera->getViewProjectionMatrix());
|
||||
|
||||
physics3dDebugCamera->apply();
|
||||
physics3dDebugCamera->clearBackground();
|
||||
|
||||
_physics3DWorld->debugDraw(renderer);
|
||||
renderer->render();
|
||||
|
||||
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
|
||||
}
|
||||
#endif
|
||||
|
||||
ax::CameraHacker::setVisitingCamera(nullptr);
|
||||
}
|
||||
|
||||
void MainScene::onTouchesBegan(const std::vector<ax::Touch*>& touches, ax::Event* event)
|
||||
{
|
||||
for (auto&& t : touches)
|
||||
@@ -272,12 +406,12 @@ void MainScene::updateCameraPosition()
|
||||
|
||||
void MainScene::onKeyPressed(EventKeyboard::KeyCode code, Event* event)
|
||||
{
|
||||
AXLOGD("Scene: #{} onKeyPressed, keycode: {}", _sceneID, static_cast<int>(code));
|
||||
_keyStates[code] = true;
|
||||
}
|
||||
|
||||
void MainScene::onKeyReleased(EventKeyboard::KeyCode code, Event* event)
|
||||
{
|
||||
AXLOGD("onKeyReleased, keycode: {}", static_cast<int>(code));
|
||||
_keyStates[code] = false;
|
||||
}
|
||||
|
||||
void MainScene::update(float delta)
|
||||
@@ -292,13 +426,43 @@ void MainScene::update(float delta)
|
||||
|
||||
case GameState::update:
|
||||
{
|
||||
/////////////////////////////
|
||||
// Add your codes below...like....
|
||||
//
|
||||
// UpdateJoyStick();
|
||||
// UpdatePlayer();
|
||||
// UpdatePhysics();
|
||||
// ...
|
||||
// Movement logic for _playerCube
|
||||
if (_playerCube)
|
||||
{
|
||||
float speed = 10.0f * delta;
|
||||
Vec3 move(0, 0, 0);
|
||||
|
||||
if (_keyStates[EventKeyboard::KeyCode::KEY_W] || _keyStates[EventKeyboard::KeyCode::KEY_UP_ARROW]) move.z -= 1.0f;
|
||||
if (_keyStates[EventKeyboard::KeyCode::KEY_S] || _keyStates[EventKeyboard::KeyCode::KEY_DOWN_ARROW]) move.z += 1.0f;
|
||||
if (_keyStates[EventKeyboard::KeyCode::KEY_A] || _keyStates[EventKeyboard::KeyCode::KEY_LEFT_ARROW]) move.x -= 1.0f;
|
||||
if (_keyStates[EventKeyboard::KeyCode::KEY_D] || _keyStates[EventKeyboard::KeyCode::KEY_RIGHT_ARROW]) move.x += 1.0f;
|
||||
|
||||
if (move != Vec3::ZERO)
|
||||
{
|
||||
move.normalize();
|
||||
Vec3 currentPos = _playerCube->getPosition3D();
|
||||
Vec3 newPos = currentPos + (move * speed);
|
||||
|
||||
// Boundary check against ground
|
||||
// gridCenter = (5.625, 0, 5.625)
|
||||
// groundSize = 14.25
|
||||
// cube half-size = 0.5
|
||||
float halfGround = 14.25f / 2.0f;
|
||||
float minX = 5.625f - halfGround + 0.5f; // -1.0
|
||||
float maxX = 5.625f + halfGround - 0.5f; // 12.25
|
||||
float minZ = 5.625f - halfGround + 0.5f; // -1.0
|
||||
float maxZ = 5.625f + halfGround - 0.5f; // 12.25
|
||||
|
||||
newPos.x = std::clamp(newPos.x, minX, maxX);
|
||||
newPos.z = std::clamp(newPos.z, minZ, maxZ);
|
||||
|
||||
_playerCube->setPosition3D(newPos);
|
||||
|
||||
// Camera follows the player
|
||||
_targetPos = newPos;
|
||||
updateCameraPosition();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class MainScene : public ax::Scene
|
||||
public:
|
||||
bool init() override;
|
||||
void update(float delta) override;
|
||||
void render(ax::Renderer* renderer, const ax::Mat4& eyeTransform, const ax::Mat4* eyeProjection = nullptr) override;
|
||||
|
||||
// touch
|
||||
void onTouchesBegan(const std::vector<ax::Touch*>& touches, ax::Event* event);
|
||||
@@ -73,8 +74,14 @@ private:
|
||||
ax::EventListenerMouse* _mouseListener = nullptr;
|
||||
int _sceneID = 0;
|
||||
|
||||
// Game Objects
|
||||
ax::MeshRenderer* _playerCube = nullptr;
|
||||
ax::DrawNode* _miniMapBorder = nullptr;
|
||||
std::map<ax::EventKeyboard::KeyCode, bool> _keyStates;
|
||||
|
||||
// Camera Orbit State
|
||||
ax::Camera* _camera3D = nullptr;
|
||||
ax::Camera* _miniMapCamera = nullptr;
|
||||
ax::Vec3 _targetPos = ax::Vec3::ZERO;
|
||||
float _pitch = 45.0f; // degrees
|
||||
float _yaw = 45.0f; // degrees
|
||||
|
||||
Reference in New Issue
Block a user