Compare commits

...

3 Commits

Author SHA1 Message Date
39acbac09e 탑다운 미니맵 구현 2026-03-09 00:09:20 +09:00
ddbe89615d 라이트 위치 표시 2026-03-08 12:21:33 +09:00
b8f05b42b3 가운데 하나만 빼고 큐브 제거 2026-03-08 12:18:17 +09:00
2 changed files with 210 additions and 28 deletions

View File

@@ -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;
@@ -81,30 +89,41 @@ bool MainScene::init()
float gridWidth = (gridSize - 1) * step;
Vec3 gridCenter(gridWidth / 2.0f, 0.0f, gridWidth / 2.0f);
// Create 10x10 cubes using MeshRenderer and the generated cube.obj
for (int i = 0; i < gridSize; ++i)
// Create a single cube at the center of the grid area
_playerCube = MeshRenderer::create("cube.obj");
if (_playerCube)
{
for (int j = 0; j < gridSize; ++j)
{
auto cube = MeshRenderer::create("cube.obj");
if (cube)
{
cube->setPosition3D(Vec3(i * step, 0.0f, j * step));
cube->setCameraMask((unsigned short)CameraFlag::USER1);
this->addChild(cube);
}
}
_playerCube->setPosition3D(gridCenter);
_playerCube->setCameraMask(cameraMask3D);
this->addChild(_playerCube);
}
// Add Lights
// Directional Light: Like sunlight, creating shadows/depth
auto dirLight = DirectionLight::create(Vec3(-1.0f, -1.0f, -0.5f), Color3B::WHITE);
dirLight->setCameraMask((unsigned short)CameraFlag::USER1);
Vec3 lightDir(-1.0f, -1.0f, -0.5f);
auto dirLight = DirectionLight::create(lightDir, Color3B::WHITE);
dirLight->setCameraMask(cameraMask3D);
this->addChild(dirLight);
// Visual marker for the Directional Light source
auto lightMarker = MeshRenderer::create("cube.obj");
if (lightMarker)
{
// Normalize direction and place the marker 20 units away from the center
Vec3 normalizedDir = lightDir;
normalizedDir.normalize();
Vec3 markerPos = gridCenter - (normalizedDir * 20.0f);
lightMarker->setPosition3D(markerPos);
lightMarker->setScale(0.5f);
lightMarker->setColor(Color3B::YELLOW);
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
@@ -121,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;
@@ -139,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);
@@ -147,11 +197,17 @@ 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)
// Add a label for confirmation
auto label = Label::createWithTTF("10x10 Cube Grid", "fonts/Marker Felt.ttf", 24);
auto label = Label::createWithTTF("Center Cube", "fonts/Marker Felt.ttf", 24);
if (label)
{
label->setPosition(Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height - 30));
@@ -163,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)
@@ -261,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)
@@ -281,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;
}

View File

@@ -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