diff --git a/Content/cube.obj b/Content/cube.obj new file mode 100644 index 0000000..1497666 --- /dev/null +++ b/Content/cube.obj @@ -0,0 +1,21 @@ +v -0.5 -0.5 0.5 +v 0.5 -0.5 0.5 +v 0.5 0.5 0.5 +v -0.5 0.5 0.5 +v -0.5 -0.5 -0.5 +v 0.5 -0.5 -0.5 +v 0.5 0.5 -0.5 +v -0.5 0.5 -0.5 + +f 1 2 3 +f 1 3 4 +f 8 7 6 +f 8 6 5 +f 4 3 7 +f 4 7 8 +f 5 1 4 +f 5 4 8 +f 5 6 2 +f 5 2 1 +f 2 6 7 +f 2 7 3 diff --git a/Source/MainScene.cpp b/Source/MainScene.cpp index 1ab6214..0377db0 100644 --- a/Source/MainScene.cpp +++ b/Source/MainScene.cpp @@ -51,92 +51,83 @@ bool MainScene::init() auto visibleSize = _director->getVisibleSize(); auto origin = _director->getVisibleOrigin(); auto safeArea = _director->getSafeAreaRect(); - auto safeOrigin = safeArea.origin; - ///////////////////////////// - // 2. add a menu item with "X" image, which is clicked to quit the program - // you may modify it. - - // add a "close" icon to exit the progress. it's an autorelease object + // 2. Add a menu item with "X" image, which is clicked to quit the program auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", AX_CALLBACK_1(MainScene::menuCloseCallback, this)); - if (closeItem == nullptr || closeItem->getContentSize().width <= 0 || closeItem->getContentSize().height <= 0) + if (closeItem != nullptr) { - problemLoading("'CloseNormal.png' and 'CloseSelected.png'"); - } - else - { - float x = safeOrigin.x + safeArea.size.width - closeItem->getContentSize().width / 2; - float y = safeOrigin.y + closeItem->getContentSize().height / 2; + float x = origin.x + visibleSize.width - closeItem->getContentSize().width / 2; + float y = origin.y + closeItem->getContentSize().height / 2; closeItem->setPosition(Vec2(x, y)); } - // create menu, it's an autorelease object auto menu = Menu::create(closeItem, NULL); menu->setPosition(Vec2::ZERO); this->addChild(menu, 1); ///////////////////////////// - // 3. add your codes below... + // 3. 3D Setup + + // Grid settings + const int gridSize = 10; + const float cubeDim = 1.0f; + const float spacing = 0.1f; + const float step = cubeDim + spacing; + + // Calculate center of the grid + // Grid goes from 0 to (gridSize-1)*step + float gridWidth = (gridSize - 1) * step; + Vec3 gridCenter(gridWidth / 2.0f, 0.0f, gridWidth / 2.0f); - // Some templates (uncomment what you need) - _touchListener = EventListenerTouchAllAtOnce::create(); - _touchListener->onTouchesBegan = AX_CALLBACK_2(MainScene::onTouchesBegan, this); - _touchListener->onTouchesMoved = AX_CALLBACK_2(MainScene::onTouchesMoved, this); - _touchListener->onTouchesEnded = AX_CALLBACK_2(MainScene::onTouchesEnded, this); - _eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this); - - //_mouseListener = EventListenerMouse::create(); - //_mouseListener->onMouseMove = AX_CALLBACK_1(MainScene::onMouseMove, this); - //_mouseListener->onMouseUp = AX_CALLBACK_1(MainScene::onMouseUp, this); - //_mouseListener->onMouseDown = AX_CALLBACK_1(MainScene::onMouseDown, this); - //_mouseListener->onMouseScroll = AX_CALLBACK_1(MainScene::onMouseScroll, this); - //_eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this); - - _keyboardListener = EventListenerKeyboard::create(); - _keyboardListener->onKeyPressed = AX_CALLBACK_2(MainScene::onKeyPressed, this); - _keyboardListener->onKeyReleased = AX_CALLBACK_2(MainScene::onKeyReleased, this); - _eventDispatcher->addEventListenerWithFixedPriority(_keyboardListener, 11); - - // add a label shows "Hello World" - // create and initialize a label - - auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24); - if (label == nullptr) + // Create 10x10 cubes using MeshRenderer and the generated cube.obj + for (int i = 0; i < gridSize; ++i) { - problemLoading("'fonts/Marker Felt.ttf'"); + 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); + } + } } - else - { - // position the label on the center of the screen - label->setPosition( - Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height - label->getContentSize().height)); - // add the label as a child to this layer + // Setup Camera for Quarter View (Isometric-like) + _camera3D = Camera::createPerspective(60.0f, visibleSize.width / visibleSize.height, 0.1f, 1000.0f); + _camera3D->setCameraFlag(CameraFlag::USER1); + + // Initial camera position setup + _targetPos = gridCenter; + _pitch = 45.0f; + _yaw = 45.0f; + _distance = 25.0f; + updateCameraPosition(); + + this->addChild(_camera3D); + + // Mouse Listener + _mouseListener = EventListenerMouse::create(); + _mouseListener->onMouseMove = AX_CALLBACK_1(MainScene::onMouseMove, this); + _mouseListener->onMouseUp = AX_CALLBACK_1(MainScene::onMouseUp, this); + _mouseListener->onMouseDown = AX_CALLBACK_1(MainScene::onMouseDown, this); + _mouseListener->onMouseScroll = AX_CALLBACK_1(MainScene::onMouseScroll, this); + _eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, 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); + if (label) + { + label->setPosition(Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height - 30)); this->addChild(label, 1); } - // add "HelloWorld" splash screen" - auto sprite = Sprite::create("HelloWorld.png"sv); - if (sprite == nullptr) - { - problemLoading("'HelloWorld.png'"); - } - else - { - // position the sprite on the center of the screen - sprite->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y)); - // add the sprite as a child to this layer - this->addChild(sprite, 0); - auto drawNode = DrawNode::create(); - drawNode->setPosition(Vec2(0, 0)); - addChild(drawNode); - - drawNode->drawRect(safeArea.origin + Vec2(1, 1), safeArea.origin + safeArea.size, Color4F::BLUE); - } - - // scheduleUpdate() is required to ensure update(float) is called on every loop scheduleUpdate(); return true; @@ -169,31 +160,75 @@ void MainScene::onTouchesEnded(const std::vector& touches, ax::Event bool MainScene::onMouseDown(Event* event) { EventMouse* e = static_cast(event); - // AXLOGD("onMouseDown detected, button: {}", static_cast(e->getMouseButton())); + if (e->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT) + { + _isMouseDown = true; + _lastMousePos = e->getLocationInView(); + } return true; } bool MainScene::onMouseUp(Event* event) { EventMouse* e = static_cast(event); - AXLOGD("onMouseUp detected, button: {}", static_cast(e->getMouseButton())); + if (e->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT) + { + _isMouseDown = false; + } return true; } bool MainScene::onMouseMove(Event* event) { EventMouse* e = static_cast(event); - // AXLOGD("onMouseMove detected, X:{} Y:{}", e->getLocation().x, e->getLocation().y); + Vec2 currentPos = e->getLocationInView(); + + if (_isMouseDown) + { + Vec2 delta = currentPos - _lastMousePos; + float sensitivity = 0.15f; // 감도 조절 (기존 0.5f에서 하향) + + _yaw += delta.x * sensitivity; + _pitch += delta.y * sensitivity; // 마우스 이동 방향에 맞춰 상하 각도 변경 + + // Clamp pitch to avoid flipping (수직 방향 각도 제한) + _pitch = std::clamp(_pitch, 10.0f, 85.0f); + + updateCameraPosition(); + } + _lastMousePos = currentPos; return true; } bool MainScene::onMouseScroll(Event* event) { EventMouse* e = static_cast(event); - // AXLOGD("onMouseScroll detected, X:{} Y:{}", e->getScrollX(), e->getScrollY()); + float zoomSensitivity = 0.5f; // 줌 감도 조절 + _distance -= e->getScrollY() * zoomSensitivity; + + // Clamp distance + _distance = std::clamp(_distance, 5.0f, 100.0f); + + updateCameraPosition(); return true; } +void MainScene::updateCameraPosition() +{ + if (!_camera3D) return; + + // Convert spherical coordinates to Cartesian + float pitchRad = AX_DEGREES_TO_RADIANS(_pitch); + float yawRad = AX_DEGREES_TO_RADIANS(_yaw); + + float x = _distance * cosf(pitchRad) * cosf(yawRad); + float y = _distance * sinf(pitchRad); + float z = _distance * cosf(pitchRad) * sinf(yawRad); + + _camera3D->setPosition3D(_targetPos + Vec3(x, y, z)); + _camera3D->lookAt(_targetPos, Vec3(0, 1, 0)); +} + void MainScene::onKeyPressed(EventKeyboard::KeyCode code, Event* event) { AXLOGD("Scene: #{} onKeyPressed, keycode: {}", _sceneID, static_cast(code)); diff --git a/Source/MainScene.h b/Source/MainScene.h index c4c30f9..5b5485e 100644 --- a/Source/MainScene.h +++ b/Source/MainScene.h @@ -65,10 +65,21 @@ public: ~MainScene() override; private: + void updateCameraPosition(); + GameState _gameState = GameState::init; ax::EventListenerTouchAllAtOnce* _touchListener = nullptr; ax::EventListenerKeyboard* _keyboardListener = nullptr; ax::EventListenerMouse* _mouseListener = nullptr; int _sceneID = 0; + + // Camera Orbit State + ax::Camera* _camera3D = nullptr; + ax::Vec3 _targetPos = ax::Vec3::ZERO; + float _pitch = 45.0f; // degrees + float _yaw = 45.0f; // degrees + float _distance = 25.0f; + bool _isMouseDown = false; + ax::Vec2 _lastMousePos = ax::Vec2::ZERO; };