CRM64Pro GDK v0.80.0
A free cross-platform game development kit built on top of SDL 3.0
Loading...
Searching...
No Matches
Tutorial 16.2: Scene Island Game v2

Overview

Island Game v2 loads a Tiled island, creates the player through an ISceneObjectFactory, builds a runtime movement-cost layer from an image and uses A* to move across the map. It adds a free camera to the simpler v1 example: press C to switch modes, then use the arrow keys to pan.

Final result

CRM64Pro Tutorial 16.2 Scene Island Game v2

Runtime controls

  • Right-click: Find an A* path to the selected cell and return to follow mode.
  • Left-click: Report the selected Scene object.
  • C: Switch between player-follow and free camera modes.
  • Arrow keys: Pan the camera while free mode is active.
  • F1: Show or hide the tile grid.
  • F2: Show or hide movement costs.
  • S: Enable or disable smooth scrolling.
  • Q or ESC: Exit the tutorial.

Prerequisites

  • CRM64Pro GDK installed and configured with a supported C++17 compiler.
  • Tutorial package downloaded and fully extracted, preserving its folder structure.
  • Complete Tutorial 16.1 or understand Scene layers and TMX object factories.
  • Included assets available under bin/Base/: tiled/rpg/island.tmx, tiled/rpg/island-heightmap.png, sprite2.png.
  • Keep the Tutorial_bg resource from Tutorial 02 in Tutorial.cdc.

What you will learn

  • Load a TMX map with a custom Scene object factory.
  • Derive a runtime tile layer from image brightness.
  • Use weighted four-direction A* pathfinding.
  • Move and animate a SceneObject along a tile path.
  • Pick cells and objects through Scene coordinate conversion.
  • Switch a Scene camera between snap-follow and manual modes.
  • Save the resulting Scene and release owned resources safely.

Island Game v1: the simpler reference

Tutorial_16_Scene_IslandGame_v1.cpp uses the same island, runtime cost layer, A* pathfinding and player interaction. Its camera always follows the player. Start with v1 when learning the pathfinding flow; use v2 when you also need to study follow/manual camera switching.

View the Island Game v1 source.

Walkthrough

1. Keep shared state small

The callbacks and custom Scene objects share only the Scene, background, debug font, tile size and current camera mode.

struct TutorialState
{
Scene* pScene = nullptr;
Sint32 idBgImage = -1;
Sint32 idDebugFont = -1;
Sint32 iTileSize = 16;
bool bCameraFollow = true;
};

2. Create the player from TMX

The factory replaces objects whose TMX type is start with Player. Returning null leaves every other object to the Scene's default implementation.

class TutorialObjectFactory : public ISceneObjectFactory
{
public:
SceneObject* create(const string& rType) override
{
if(rType == "start")
{
return new(std::nothrow) Player();
}
return nullptr; // Return null to let Scene create a generic object
}
};

3. Load the island Scene

Pass the factory while loading so object construction happens as the TMX is parsed. Setup stops if either the load or manager lookup fails.

static TutorialObjectFactory Factory;
string sTMX = BASEDIR "tiled/rpg/island.tmx";
Sint32 idScene = mC64.sceneMgr().loadFromFile(sTMX, &Factory);
if(idScene < 0)
{
mLog.msg(LL_CRITICAL, "Failed to load TMX: %s\n", sTMX.c_str());
return false;
}
// Retrieve the engine pointer from the Manager
g_pState->pScene = mC64.sceneMgr().get(idScene);
if(!g_pState->pScene) return false;

4. Add the runtime cost layer

Layer 1 supplies the grid dimensions and tile size. The custom layer is added at index 5, kept active for pathfinding and hidden until F2 is pressed.

// 2. Create Layer 5 (HeightMap) from Layer 1 dimensions.
SceneLayerTile* pLayer1 = g_pState->pScene->accessLayerTile(1);
if(!pLayer1)
{
mLog.msg(LL_ERROR, "Required tile layer 1 was not found.\n");
return false;
}
// Use tile size from loaded layer 1 as source of truth.
const Sint32 iMapW = pLayer1->getWidth();
const Sint32 iMapH = pLayer1->getHeight();
g_pState->iTileSize = pLayer1->getCellWidth();
if(iMapW <= 0 || iMapH <= 0 || g_pState->iTileSize <= 0)
{
mLog.msg(LL_ERROR, "Tile layer 1 has invalid dimensions.\n");
return false;
}
SceneLayerHeightMap* pLayerHM = new(std::nothrow) SceneLayerHeightMap("HeightMap", iMapH, iMapW);
if(!pLayerHM)
{
mLog.msg(LL_ERROR, "Failed to allocate the heightmap layer.\n");
return false;
}
if(!pLayerHM->setCellWidth(g_pState->iTileSize) ||
!pLayerHM->setCellHeight(g_pState->iTileSize))
{
delete pLayerHM;
mLog.msg(LL_ERROR, "Failed to configure the heightmap cell size.\n");
return false;
}
// Add to Engine at index 5
if(g_pState->pScene->addLayer(pLayerHM, 5) < 0)
{
delete pLayerHM;
mLog.msg(LL_ERROR, "Failed to add the heightmap layer.\n");
return false;
}

5. Convert image brightness to movement cost

Each tile samples the center of the heightmap image. Black becomes blocked cost 255; brighter pixels produce progressively lower costs, down to 1.

Uint8 iR = 0, iG = 0, iB = 0, iA = 0;
if(!SDL_ReadSurfacePixel(sH, iPX, iPY, &iR, &iG, &iB, &iA))
{
mC64.imageMgr().close(idImgH);
mLog.msg(LL_ERROR, "Failed to read Heightmap image pixels.\n");
return false;
}
const Sint32 iBrightness = (iR + iG + iB) / 3;
// Black is blocked; brighter pixels have lower movement costs.
Uint32 iWeight = 255;
if(iBrightness > 10)
{
iWeight = 255 - iBrightness;
if(iWeight < 1) iWeight = 1;
}
pLayerHM->setCellValue(iY, iX, iWeight);

6. Build a weighted A* search

The open set prioritizes total estimated cost. Four cardinal neighbors are accepted when they are inside the map and not marked with cost 255.

priority_queue<Node> vOpenSet;
vector<vector<float>> vvfCostSoFar(iHeight, vector<float>(iWidth, -1.0f));
vector<vector<SDL_Point>> vvCameFrom(iHeight, vector<SDL_Point>(iWidth, { -1, -1 }));
vOpenSet.push({ iStartX, iStartY, 0.0f });
vvfCostSoFar[iStartY][iStartX] = 0.0f;
// 4-Way movement only (No diagonals)
const Sint32 iDirs[4][2] = { {0,1}, {1,0}, {0,-1}, {-1,0} };
Uint32 iWeight = pMap->iCellMap[iNY][iNX];
if(iWeight == 255) continue; // Blocked
// Distance is always 1.0 for cardinal
float fDist = 1.0f;
// Weight 1 = Normal cost. Higher weight = Higher cost.
float fNewCost = vvfCostSoFar[mCurrent.iY][mCurrent.iX] + (fDist * (float)iWeight);
if(vvfCostSoFar[iNY][iNX] == -1.0f || fNewCost < vvfCostSoFar[iNY][iNX])
{
vvfCostSoFar[iNY][iNX] = fNewCost;
float fPriority = fNewCost + (abs(iEndX - iNX) + abs(iEndY - iNY));
vOpenSet.push({ iNX, iNY, fPriority });
vvCameFrom[iNY][iNX] = { mCurrent.iX, mCurrent.iY };
}

7. Spend movement across path nodes

When one node is reached, the loop keeps the unused movement budget and continues with the next node. This keeps motion stable at different logic rates.

if(fDist <= fStepRemaining) // Reach node and keep remaining budget
{
setPosition(fNextWorldX, fNextWorldY);
fStepRemaining -= fDist;
iCurrentPathIndex++;
if(iCurrentPathIndex >= (int)vPath.size())
{
bIsMoving = false;
vPath.clear();
// Pause animation on stop
if(pS) pS->pause();
return;
}
continue;
}

8. Require the player object

The player is essential for movement and camera following. The exit remains optional because the rest of the island is still usable without it.

if(!pPlayer)
{
mC64.logMgr().get()->msg(LL_ERROR, "Player object ('start') not found in Layer 3.\n");
closeTutorial();
return -1;
}
if(!pExit)
{
mC64.logMgr().get()->msg(LL_INFO, "Exit area object ('exit') not found in Layer 3.\n");
}

9. Configure the follow camera

The default camera snaps to the player, centers its target and clamps the viewport to the map bounds.

SceneCameraParams mCam;
mCam.bClampToBounds = true;
mCam.fDamping = 0.0f;
mCam.ptTargetAnchor = { 0.5f, 0.5f };
mCam.ptScreenAnchor = { 0.5f, 0.5f };
if(!g_pState->pScene->setCameraTarget(1, pPlayer) ||
!g_pState->pScene->setCameraParams(1, mCam) ||
!g_pState->pScene->setCameraMode(1, SCM_SNAP))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to configure the Scene camera.\n");
closeTutorial();
return -1;
}

10. Switch camera modes

C selects either snap-follow or manual mode. In manual mode, the arrow keys move layer 1, which acts as the camera anchor for the Scene.

if(ev.key.key == SDLK_C)
{
g_pState->bCameraFollow = !g_pState->bCameraFollow;
if(g_pState->bCameraFollow)
{
if(pPlayer) g_pState->pScene->setCameraTarget(1, pPlayer);
g_pState->pScene->setCameraMode(1, SCM_SNAP);
}
else
{
g_pState->pScene->setCameraMode(1, SCM_MANUAL);
}
}
if(!g_pState->bCameraFollow)
{
float fDX = 0.0f;
float fDY = 0.0f;
if(mC64.getKeyState(SDLK_LEFT)) fDX -= fCameraPanStep;
if(mC64.getKeyState(SDLK_RIGHT)) fDX += fCameraPanStep;
if(mC64.getKeyState(SDLK_UP)) fDY -= fCameraPanStep;
if(mC64.getKeyState(SDLK_DOWN)) fDY += fCameraPanStep;
if(fDX != 0.0f || fDY != 0.0f)
{
float fX = 0.0f, fY = 0.0f;
if(g_pState->pScene->getLayerPosition(1, &fX, &fY) >= 0)
{
g_pState->pScene->setLayerPosition(1, Position(fX + fDX), Position(fY + fDY));
}
}
}

11. Select a destination cell

A right-click converts the pointer into a layer-1 cell, starts A* movement and returns the camera to player-follow mode.

if((iMouseB & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) && !(iMousePrev & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) && pPlayer)
{
Sint32 iGX = 0;
Sint32 iGY = 0;
if(g_pState->pScene->mouseToCell(&iGX, &iGY, 1))
{
mC64.logMgr().get()->msg(LL_INFO, "Move to: Cell(%d, %d)\n", iGX, iGY);
pPlayer->moveTo(iGX, iGY);
g_pState->bCameraFollow = true;
g_pState->pScene->setCameraTarget(1, pPlayer);
g_pState->pScene->setCameraMode(1, SCM_SNAP);
}
}

12. Handle the exit and clean up

Overlapping the exit pauses the Scene while the confirmation dialog is open. Cleanup follows ownership order and is also reused by setup failures.

if(pPlayer && pExit && pPlayer->overlapsObject(pExit))
{
pPlayer->bIsMoving = false;
pPlayer->vPath.clear();
g_pState->pScene->pause();
if(mC64.tool().messageBox("Exit Island", "Do you want to leave the island and end the game?", MBB_YES | MBB_NO, MBT_QUESTION) == MBB_YES)
{
bDone = true;
}
else
{
pPlayer->setPosition(pPlayer->getX(), pPlayer->getY() + 32.0f);
}
g_pState->pScene->resume();
}
static void closeTutorial()
{
Main& mC64 = Main::instance();
mC64.sceneMgr().close(0);
mC64.spriteMgr().close(0);
mC64.fontMgr().close(0);
mC64.imageMgr().close(0);
Main::terminate();
}

Complete source

The tutorial source is authoritative. The following listing is included directly from validation/source/Tutorial_16_Scene_IslandGame_v2.cpp.

/* ----------------------------------------------------------------------
CRM64Pro GDK - Roberto Prieto
Copyright (C) 2013-2026 MegaStorm Systems
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
------------------------------------------------------------------------
Tutorial 16 Scene IslandGame v2 (Free Camera)
------------------------------------------------------------------------
Overview:
This tutorial extends IslandGame v1 with two camera modes: follow the
player or pan the camera manually.
Key concepts:
- Load a TMX map with a Scene object factory.
- Create SceneObject instances from TMX object types.
- Add a runtime tile layer for terrain weights.
- Use A* pathfinding with tile costs.
- Switch between camera follow and manual camera movement.
- Pick objects and cells from the mouse position.
Layer layout:
- Layer 1: Base tile world (camera anchor).
- Layer 2-4: Additional visual/game layers from TMX.
- Layer 3 object layer: start/exit gameplay objects.
- Layer 5: Runtime heightmap/weights debug layer.
Controls:
- Right-Click: Move player (A* target cell).
- C: Toggle camera mode (follow/free).
- Arrow Keys: Pan camera in free mode.
- F1: Toggle grid on layer 1.
- F2: Toggle height/weights debug render.
- S: Toggle smooth scroll.
- Q / ESC: Quit.
Loop order:
- Poll events and capture mouse button edges.
- Resolve move targets and click interactions.
- Run one fixed logic tick via Scene::update().
- Let Scene update follow mode; pan manually in free mode.
- Render from callback using Scene::render().
------------------------------------------------------------------------ */
#include "CRM64Pro.h"
#include "RunParameter.h"
using namespace CRM64Pro;
// Paths used by the tutorial.
#define BASEDIR "Base/"
#ifdef CRM64PRO_PLATFORM_WINDOWS
#define OUTPUTDIR "Win64/"
#elif defined(CRM64PRO_PLATFORM_LINUX)
#define OUTPUTDIR "Linux/"
#elif defined(CRM64PRO_PLATFORM_MACOS)
#define OUTPUTDIR "macOS/"
#endif
#define RESOURCE_BG_CDC_NAME "Tutorial_bg"
#define OUTPUT_CDC OUTPUTDIR"Tutorial.cdc"
// Screen and viewport size.
static const Sint32 iScreenW = 960;
static const Sint32 iScreenH = 540;
static const Sint32 iViewW = 400;
static const Sint32 iViewH = 300;
static const Sint32 iViewX = (iScreenW - iViewW) / 2;
static const Sint32 iViewY = (iScreenH - iViewH) / 2;
// Logic timing.
static const Sint32 iLogicRate = 20;
static const float fPlayerSpeed = 60.0f;
static const float fCameraPanStep = 5.0f;
struct TutorialState
{
Scene* pScene = nullptr;
Sint32 idBgImage = -1;
Sint32 idDebugFont = -1;
Sint32 iTileSize = 16;
bool bCameraFollow = true;
};
static TutorialState* g_pState = nullptr;
// Tile layer used to store pathfinding weights.
// 255 blocks movement, 1 is normal movement, higher values are slower.
class SceneLayerHeightMap : public SceneLayerTile
{
public:
SceneLayerHeightMap(const string& sName, Sint32 iH, Sint32 iW) : SceneLayerTile(sName, iH, iW)
{
}
// Draw pathfinding weights when the debug layer is visible.
Sint32 render(Scene* pScene, SceneLayerContext& rContext) override
{
if(!g_pState) return 0;
// Layer 5 owns this runtime weight layer.
if(!(pScene->getLayerFeatures(5) & SLF_RENDER)) return 0;
Font* pFont = Main::instance().fontMgr().get(g_pState->idDebugFont);
if(!pFont) return 0;
// Only draw cells inside the current viewport.
VisibleRange mVR = calculateVisibleRange(rContext.rViewport, rContext.fScrollX, rContext.fScrollY);
for(Sint32 iLY = mVR.iCYStart; iLY < mVR.iCYEnd; iLY++)
{
if(iLY < 0 || iLY >= getHeight()) continue;
// Screen position for this row.
float fScreenY = rContext.worldToScreen(0.0f, static_cast<float>(iLY * mVR.iTileH), true).y;
for(Sint32 iLX = mVR.iCXStart; iLX < mVR.iCXEnd; iLX++)
{
if(iLX < 0 || iLX >= getWidth()) continue;
Uint32 iWeight = iCellMap[iLY][iLX];
// Keep the debug overlay readable.
if(iWeight == 0) continue;
// Screen position for this column.
float fScreenX = rContext.worldToScreen(static_cast<float>(iLX * mVR.iTileW), 0.0f, true).x;
char szBuf[16];
if(iWeight == 255) snprintf(szBuf, 16, "X");
else snprintf(szBuf, 16, "%u", static_cast<unsigned int>(iWeight));
Sint32 iFW = pFont->getWidth(szBuf);
Sint32 iFH = pFont->getHeight();
// Center the text in the tile.
pFont->setPosition(fScreenX + (mVR.iTileW - iFW) / 2, fScreenY + (mVR.iTileH - iFH) / 2);
pFont->render(szBuf);
}
}
return 0;
}
};
// A* pathfinding data.
struct Node
{
Sint32 iX, iY;
float fF; // Total cost
// Reverse comparison so priority_queue selects the lowest cost first.
bool operator<(const Node& rOther) const
{
return fF > rOther.fF;
}
};
// Player object.
class Player : public SceneObject
{
public:
static Sint32 iIDSpriteShared; // Static to share sprite resource across instances
Sint32 iIDSprite;
bool bIsMoving;
vector<SDL_Point> vPath; // Grid coordinates path
Sint32 iCurrentPathIndex;
Player() : iIDSprite(-1), bIsMoving(false), iCurrentPathIndex(0) {
}
void initialize() override
{
// Explicitly set size to match the visual sprite (32x32).
setSize(32.0f, 32.0f);
// Adjust initial position from TMX anchor (Center-Bottom) to Top-Left
setPosition(getX() - 16.0f, getY() - 32.0f);
// Load the shared Sprite resource once.
if(iIDSpriteShared < 0)
{
ImageMgr& mImageMgr = Main::instance().imageMgr();
SpriteMgr& mSpriteMgr = Main::instance().spriteMgr();
string sSprName = "PlayerSprite";
iIDSpriteShared = mSpriteMgr.create(sSprName);
if(iIDSpriteShared >= 0)
{
Sint32 idImg = mImageMgr.loadFromFile(BASEDIR "sprite2.png");
if(idImg > 0)
{
Sprite* pSpr = mSpriteMgr.get(iIDSpriteShared);
pSpr->assignImage(idImg, 1);
pSpr->setOffset(32, 32);
for(Sint32 i = 0; i <= 3; i++) pSpr->setFrameTimeIndex(i, -1, 50);
// Initial state: left and paused.
pSpr->pause();
}
}
}
iIDSprite = iIDSpriteShared;
}
void updateMovement(float fDeltaTime)
{
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(!bIsMoving || vPath.empty())
{
// Ensure animation is paused when not moving
if(pS && pS->status() != GS_PAUSED) pS->pause();
return;
}
float fStepRemaining = fPlayerSpeed * fDeltaTime;
const float fEpsilon = 0.0001f;
while(bIsMoving && !vPath.empty() && iCurrentPathIndex < (int)vPath.size() && fStepRemaining > fEpsilon)
{
// Convert grid point to world center aligned with Object Center
// Target is Top-Left of the sprite area.
float fNextWorldX = (float)(vPath[iCurrentPathIndex].x * g_pState->iTileSize) + (g_pState->iTileSize * 0.5f) - 16.0f;
float fNextWorldY = (float)(vPath[iCurrentPathIndex].y * g_pState->iTileSize) + (float)g_pState->iTileSize - 32.0f;
float fDiffX = fNextWorldX - getX();
float fDiffY = fNextWorldY - getY();
const bool bMostlyHorizontal = fabsf(fDiffX) >= fabsf(fDiffY);
if(bMostlyHorizontal) fDiffY = 0.0f;
else fDiffX = 0.0f;
float fDist = sqrtf(fDiffX * fDiffX + fDiffY * fDiffY);
if(fDist <= fEpsilon)
{
iCurrentPathIndex++;
continue;
}
if(fDist <= fStepRemaining) // Reach node and keep remaining budget
{
setPosition(fNextWorldX, fNextWorldY);
fStepRemaining -= fDist;
iCurrentPathIndex++;
if(iCurrentPathIndex >= (int)vPath.size())
{
bIsMoving = false;
vPath.clear();
// Pause animation on stop
if(pS) pS->pause();
return;
}
continue;
}
// Normalize and move by remaining budget
float fMoveX = (fDiffX / fDist) * fStepRemaining;
float fMoveY = (fDiffY / fDist) * fStepRemaining;
float fNewX = getX() + fMoveX;
float fNewY = getY() + fMoveY;
// Keep the orthogonal axis stable to avoid sub-pixel drift on cardinal paths.
if(bMostlyHorizontal) fNewY = fNextWorldY;
else fNewX = fNextWorldX;
setPosition(fNewX, fNewY);
fStepRemaining = 0.0f;
// Update Animation based on direction
if(pS)
{
Sint32 iState = SPR_STATE_DOWN;
if(fDiffX > 0) iState = SPR_STATE_RIGHT;
else if(fDiffX < 0) iState = SPR_STATE_LEFT;
else if(fDiffY > 0) iState = SPR_STATE_DOWN;
else if(fDiffY < 0) iState = SPR_STATE_UP;
// Only change state if different, to avoid resetting frame
if(pS->getAnimCurrent() != iState || pS->status() == GS_PAUSED)
{
pS->selectAnim(iState);
pS->resume();
}
}
}
}
void update(Scene*, SceneLayerContext& rContext) override
{
updateMovement(rContext.fDeltaTime);
}
// Draw the player sprite at the object position.
void render(Scene* pScene, SceneLayerContext& rContext) override
{
if(iIDSprite < 0) return;
Sprite* pSpr = Main::instance().spriteMgr().get(iIDSprite);
if(!pSpr) return;
SDL_FPoint pScreen = rContext.worldToScreen(getX(), getY(), true);
float fScreenX = pScreen.x;
float fScreenY = pScreen.y;
if(rContext.data.Object.bRenderStateValid && rContext.data.Object.pObject == this)
{
fScreenX = rContext.data.Object.fScreenX;
fScreenY = rContext.data.Object.fScreenY;
}
pSpr->setPosition(fScreenX, fScreenY);
pSpr->render();
}
void moveTo(Sint32 iGridX, Sint32 iGridY);
};
Sint32 Player::iIDSpriteShared = -1;
// Object factory.
class TutorialObjectFactory : public ISceneObjectFactory
{
public:
SceneObject* create(const string& rType) override
{
if(rType == "start")
{
return new(std::nothrow) Player();
}
return nullptr; // Return null to let Scene create a generic object
}
};
// A* implementation.
void Player::moveTo(Sint32 iEndX, Sint32 iEndY)
{
if(!g_pState || !g_pState->pScene) return;
// Access HeightMap (Layer 5)
SceneLayerTile* pMap = g_pState->pScene->accessLayerTile(5);
if(!pMap) return;
// Use the center-bottom of the player (the "feet") to determine the current grid cell.
Sint32 iStartX = (Sint32)((getX() + getWidth() * 0.5f) / static_cast<float>(g_pState->iTileSize));
Sint32 iStartY = (Sint32)((getY() + getHeight() - 1.0f) / static_cast<float>(g_pState->iTileSize));
Sint32 iWidth = pMap->getWidth();
Sint32 iHeight = pMap->getHeight();
// Bounds check
if(iStartX < 0 || iStartX >= iWidth || iStartY < 0 || iStartY >= iHeight) return;
if(iEndX < 0 || iEndX >= iWidth || iEndY < 0 || iEndY >= iHeight) return;
// Check target walkable (255 = blocked)
if(pMap->iCellMap[iEndY][iEndX] == 255) return;
// A* Data
priority_queue<Node> vOpenSet;
vector<vector<float>> vvfCostSoFar(iHeight, vector<float>(iWidth, -1.0f));
vector<vector<SDL_Point>> vvCameFrom(iHeight, vector<SDL_Point>(iWidth, { -1, -1 }));
vOpenSet.push({ iStartX, iStartY, 0.0f });
vvfCostSoFar[iStartY][iStartX] = 0.0f;
// 4-Way movement only (No diagonals)
const Sint32 iDirs[4][2] = { {0,1}, {1,0}, {0,-1}, {-1,0} };
while(!vOpenSet.empty())
{
Node mCurrent = vOpenSet.top();
vOpenSet.pop();
if(mCurrent.iX == iEndX && mCurrent.iY == iEndY) break;
for(auto& rDir : iDirs)
{
Sint32 iNX = mCurrent.iX + rDir[0];
Sint32 iNY = mCurrent.iY + rDir[1];
if(iNX >= 0 && iNX < iWidth && iNY >= 0 && iNY < iHeight)
{
Uint32 iWeight = pMap->iCellMap[iNY][iNX];
if(iWeight == 255) continue; // Blocked
// Distance is always 1.0 for cardinal
float fDist = 1.0f;
// Weight 1 = Normal cost. Higher weight = Higher cost.
float fNewCost = vvfCostSoFar[mCurrent.iY][mCurrent.iX] + (fDist * (float)iWeight);
if(vvfCostSoFar[iNY][iNX] == -1.0f || fNewCost < vvfCostSoFar[iNY][iNX])
{
vvfCostSoFar[iNY][iNX] = fNewCost;
float fPriority = fNewCost + (abs(iEndX - iNX) + abs(iEndY - iNY));
vOpenSet.push({ iNX, iNY, fPriority });
vvCameFrom[iNY][iNX] = { mCurrent.iX, mCurrent.iY };
}
}
}
}
// Reconstruct
vPath.clear();
if(vvCameFrom[iEndY][iEndX].x != -1)
{
SDL_Point mCurr = { iEndX, iEndY };
while(mCurr.x != iStartX || mCurr.y != iStartY)
{
vPath.push_back(mCurr);
mCurr = vvCameFrom[mCurr.y][mCurr.x];
}
reverse(vPath.begin(), vPath.end());
iCurrentPathIndex = 0;
bIsMoving = true;
// Ensure animation starts
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS) pS->resume();
}
}
// Setup function.
bool setupTutorial()
{
if(!g_pState) return false;
Main& mC64 = Main::instance();
Log& mLog = *mC64.logMgr().get();
// 1. Set Object factory and load TMX in one go
static TutorialObjectFactory Factory;
string sTMX = BASEDIR "tiled/rpg/island.tmx";
Sint32 idScene = mC64.sceneMgr().loadFromFile(sTMX, &Factory);
if(idScene < 0)
{
mLog.msg(LL_CRITICAL, "Failed to load TMX: %s\n", sTMX.c_str());
return false;
}
// Retrieve the engine pointer from the Manager
g_pState->pScene = mC64.sceneMgr().get(idScene);
if(!g_pState->pScene) return false;
// 2. Create Layer 5 (HeightMap) from Layer 1 dimensions.
SceneLayerTile* pLayer1 = g_pState->pScene->accessLayerTile(1);
if(!pLayer1)
{
mLog.msg(LL_ERROR, "Required tile layer 1 was not found.\n");
return false;
}
// Use tile size from loaded layer 1 as source of truth.
const Sint32 iMapW = pLayer1->getWidth();
const Sint32 iMapH = pLayer1->getHeight();
g_pState->iTileSize = pLayer1->getCellWidth();
if(iMapW <= 0 || iMapH <= 0 || g_pState->iTileSize <= 0)
{
mLog.msg(LL_ERROR, "Tile layer 1 has invalid dimensions.\n");
return false;
}
SceneLayerHeightMap* pLayerHM = new(std::nothrow) SceneLayerHeightMap("HeightMap", iMapH, iMapW);
if(!pLayerHM)
{
mLog.msg(LL_ERROR, "Failed to allocate the heightmap layer.\n");
return false;
}
if(!pLayerHM->setCellWidth(g_pState->iTileSize) ||
!pLayerHM->setCellHeight(g_pState->iTileSize))
{
delete pLayerHM;
mLog.msg(LL_ERROR, "Failed to configure the heightmap cell size.\n");
return false;
}
// Add to Engine at index 5
if(g_pState->pScene->addLayer(pLayerHM, 5) < 0)
{
delete pLayerHM;
mLog.msg(LL_ERROR, "Failed to add the heightmap layer.\n");
return false;
}
// Default: Updates enabled (for logic access), Rendering disabled (F2 to toggle)
if(!g_pState->pScene->setLayerFeatures(5, SLF_UPDATE, true) ||
!g_pState->pScene->setLayerFeatures(5, SLF_RENDER, false) ||
!g_pState->pScene->setLayerAlphaMod(5, 128))
{
mLog.msg(LL_ERROR, "Failed to configure the heightmap layer.\n");
return false;
}
// 3. Load weights from Image
// "island-heightmap.png" - Black (0) = Blocked, White (255) = Walkable
string sHMFile = BASEDIR "tiled/rpg/island-heightmap.png";
Sint32 idImgH = mC64.imageMgr().loadFromFile(sHMFile);
if(idImgH <= 0)
{
mLog.msg(LL_ERROR, "Failed to load Heightmap image: %s\n", sHMFile.c_str());
return false;
}
Image* pHeightImage = mC64.imageMgr().get(idImgH);
SDL_Surface* sH = pHeightImage ? pHeightImage->getSurface() : nullptr;
if(!sH || sH->w <= 0 || sH->h <= 0)
{
mC64.imageMgr().close(idImgH);
mLog.msg(LL_ERROR, "Failed to access Heightmap image pixels.\n");
return false;
}
for(Sint32 iY = 0; iY < iMapH; iY++)
{
for(Sint32 iX = 0; iX < iMapW; iX++)
{
// Sample the center of each tile.
Sint32 iPX = iX * g_pState->iTileSize + (g_pState->iTileSize / 2);
Sint32 iPY = iY * g_pState->iTileSize + (g_pState->iTileSize / 2);
if(iPX >= sH->w) iPX = sH->w - 1;
if(iPY >= sH->h) iPY = sH->h - 1;
Uint8 iR = 0, iG = 0, iB = 0, iA = 0;
if(!SDL_ReadSurfacePixel(sH, iPX, iPY, &iR, &iG, &iB, &iA))
{
mC64.imageMgr().close(idImgH);
mLog.msg(LL_ERROR, "Failed to read Heightmap image pixels.\n");
return false;
}
const Sint32 iBrightness = (iR + iG + iB) / 3;
// Black is blocked; brighter pixels have lower movement costs.
Uint32 iWeight = 255;
if(iBrightness > 10)
{
iWeight = 255 - iBrightness;
if(iWeight < 1) iWeight = 1;
}
pLayerHM->setCellValue(iY, iX, iWeight);
}
}
mC64.imageMgr().close(idImgH);
return true;
}
// Adapt the renderer to the engine callback signature.
Sint32 renderWrapper(Sint32)
{
if(!g_pState || !g_pState->pScene) return 0;
// Draw background first
if(g_pState->idBgImage > 0)
{
Image* pBG = Main::instance().imageMgr().get(g_pState->idBgImage);
if(pBG) pBG->render();
}
// Then Scene
g_pState->pScene->render();
return 0;
}
// Close resources in ownership order, including partially completed setup.
static void closeTutorial()
{
Main& mC64 = Main::instance();
mC64.sceneMgr().close(0);
mC64.spriteMgr().close(0);
mC64.fontMgr().close(0);
mC64.imageMgr().close(0);
}
// Run the tutorial application and release its resources.
int main(int argc, char* argv[])
{
if(!runParameterReadRender(argc, argv, eRenderer)) return 1;
// 1. Init
atexit(CMem::destroy);
Main& mC64 = Main::instance();
TutorialState state;
g_pState = &state;
mC64.logMgr().get()->init("Tutorial_16_Scene_IslandGame_v2", LL_INFO, LM_FILE | LM_STDOUT, OUTPUTDIR"Tutorial_16_Scene_IslandGame_v2.log");
if(!mC64.timer().init() || !mC64.timer().setRate(0, iLogicRate))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to initialize tutorial timing.\n");
return -1;
}
Screen* pScreen = mC64.configMgr().get();
pScreen->setSize(iScreenW, iScreenH);
pScreen->setDriver(eRenderer);
pScreen->setTitle("Tutorial 16 Scene - Island Game v2");
if(pScreen->show() < 0)
{
return -1;
}
if(!pScreen->setRenderCallback(renderWrapper))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to set the render callback.\n");
return -1;
}
// Load the background and debug font.
g_pState->idBgImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_BG_CDC_NAME);
g_pState->idDebugFont = mC64.fontMgr().getBuiltin("CourierNew10White");
if(g_pState->idBgImage <= 0 || g_pState->idDebugFont <= 0)
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to load the background or debug font.\n");
closeTutorial();
return -1;
}
// 2. Setup
if(!setupTutorial())
{
closeTutorial();
return -1;
}
// Find Player object (Layer 3)
Player* pPlayer = nullptr;
SceneObject* pExit = nullptr;
if(g_pState->pScene->isObjectLayer(3))
{
SceneLayerObject* pLayerObj = g_pState->pScene->accessLayerObject(3);
if(pLayerObj)
{
pPlayer = dynamic_cast<Player*>(pLayerObj->findFirstByType("start"));
pExit = pLayerObj->findFirstByType("exit");
}
}
if(!pPlayer)
{
mC64.logMgr().get()->msg(LL_ERROR, "Player object ('start') not found in Layer 3.\n");
closeTutorial();
return -1;
}
if(!pExit)
{
mC64.logMgr().get()->msg(LL_INFO, "Exit area object ('exit') not found in Layer 3.\n");
}
// Camera setup
bool bShowGrid = false;
bool bShowWeights = false;
bool bSmoothScroll = true;
// Viewport & Camera Initialization
SDL_Rect rView = { iViewX, iViewY, iViewW, iViewH };
if(!g_pState->pScene->setViewport(&rView))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to configure the Scene viewport.\n");
closeTutorial();
return -1;
}
mC64.logMgr().get()->msg(LL_INFO, "Init: Viewport set to [%d, %d, %d, %d]\n", rView.x, rView.y, rView.w, rView.h);
// Camera setup (follow by default)
mCam.bClampToBounds = true;
mCam.fDamping = 0.0f;
mCam.ptTargetAnchor = { 0.5f, 0.5f };
mCam.ptScreenAnchor = { 0.5f, 0.5f };
if(!g_pState->pScene->setCameraTarget(1, pPlayer) ||
!g_pState->pScene->setCameraParams(1, mCam) ||
!g_pState->pScene->setCameraMode(1, SCM_SNAP))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to configure the Scene camera.\n");
closeTutorial();
return -1;
}
for(Sint32 i = 1; i <= 4; i++)
{
if(!g_pState->pScene->setLayerFeatures(i, SLF_SMOOTHSCROLL, bSmoothScroll))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to enable smooth scrolling on layer %d.\n", i);
closeTutorial();
return -1;
}
}
mC64.logMgr().get()->msg(LL_INFO, "Initialization complete. Starting loop.\n");
mC64.logMgr().get()->msg(LL_INFO, "\nCONTROLS:\n [Right-Click] Move\n [Cursors] Scroll with Camera mode\n [C] Switch to Camera mode\n [S] Toggle smooth scroll\n [F1] Toggle grid\n [F2] Toggle weights\n [Q/Esc] Quit\n\n");
SDL_Event ev;
bool bDone = false;
while(!bDone)
{
while(mC64.update(&ev))
{
if(ev.type == SDL_EVENT_QUIT) bDone = true;
if(ev.type == SDL_EVENT_KEY_DOWN)
{
if(ev.key.key == SDLK_ESCAPE || ev.key.key == SDLK_Q) bDone = true;
if(ev.key.key == SDLK_C)
{
g_pState->bCameraFollow = !g_pState->bCameraFollow;
if(g_pState->bCameraFollow)
{
if(pPlayer) g_pState->pScene->setCameraTarget(1, pPlayer);
g_pState->pScene->setCameraMode(1, SCM_SNAP);
}
else
{
g_pState->pScene->setCameraMode(1, SCM_MANUAL);
}
}
if(ev.key.key == SDLK_F1)
{
bShowGrid = !bShowGrid;
g_pState->pScene->setDebugOverlay(SDO_TILE_CELLGRID, bShowGrid);
}
if(ev.key.key == SDLK_F2)
{
bShowWeights = !bShowWeights;
g_pState->pScene->setLayerFeatures(5, SLF_RENDER, bShowWeights);
}
if(ev.key.key == SDLK_S)
{
bSmoothScroll = !bSmoothScroll;
for(Sint32 i = 1; i <= 4; i++) g_pState->pScene->setLayerFeatures(i, SLF_SMOOTHSCROLL, bSmoothScroll);
}
}
}
if(bDone) break;
// Input
Sint32 iMouseB = mC64.cursorMgr().getButtons();
Sint32 iMousePrev = mC64.cursorMgr().getButtonsPrev();
if((iMouseB & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) && !(iMousePrev & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) && pPlayer)
{
Sint32 iGX = 0;
Sint32 iGY = 0;
if(g_pState->pScene->mouseToCell(&iGX, &iGY, 1))
{
mC64.logMgr().get()->msg(LL_INFO, "Move to: Cell(%d, %d)\n", iGX, iGY);
pPlayer->moveTo(iGX, iGY);
g_pState->bCameraFollow = true;
g_pState->pScene->setCameraTarget(1, pPlayer);
g_pState->pScene->setCameraMode(1, SCM_SNAP);
}
}
float fMX = mC64.cursorMgr().getX();
float fMY = mC64.cursorMgr().getY();
float fWX = 0.0f, fWY = 0.0f;
vector<SceneObject*> vHits;
// Precise hit testing
if(g_pState->pScene->screenToWorld((Sint32)fMX, (Sint32)fMY, &fWX, &fWY, 3))
{
vector<SceneObject*> vQuery;
SceneLayerObject* pLayerObj = g_pState->pScene->accessLayerObject(3);
if(pLayerObj) pLayerObj->queryRegion(fWX, fWY, 1.0f, 1.0f, vQuery);
for(auto* pObj : vQuery)
{
if(pObj->containsPoint(fWX, fWY)) vHits.push_back(pObj);
}
}
// Update cursor appearance
if(!vHits.empty()) mC64.cursorMgr().select(SDL_SYSTEM_CURSOR_POINTER);
else mC64.cursorMgr().select(SDL_SYSTEM_CURSOR_DEFAULT);
// Handle left-click interaction
if((iMouseB & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) && !(iMousePrev & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)))
{
if(!vHits.empty())
{
SceneObject* pObj = vHits.front();
mC64.logMgr().get()->msg(LL_INFO, "Hit object: '%s' [%s]\n",
pObj->getName().c_str(), pObj->getType().c_str());
}
}
g_pState->pScene->update();
// Manual/free camera mode
if(!g_pState->bCameraFollow)
{
float fDX = 0.0f;
float fDY = 0.0f;
if(mC64.getKeyState(SDLK_LEFT)) fDX -= fCameraPanStep;
if(mC64.getKeyState(SDLK_RIGHT)) fDX += fCameraPanStep;
if(mC64.getKeyState(SDLK_UP)) fDY -= fCameraPanStep;
if(mC64.getKeyState(SDLK_DOWN)) fDY += fCameraPanStep;
if(fDX != 0.0f || fDY != 0.0f)
{
float fX = 0.0f, fY = 0.0f;
if(g_pState->pScene->getLayerPosition(1, &fX, &fY) >= 0)
{
g_pState->pScene->setLayerPosition(1, Position(fX + fDX), Position(fY + fDY));
}
}
}
// Exit area logic
if(pPlayer && pExit && pPlayer->overlapsObject(pExit))
{
pPlayer->bIsMoving = false;
pPlayer->vPath.clear();
g_pState->pScene->pause();
if(mC64.tool().messageBox("Exit Island", "Do you want to leave the island and end the game?", MBB_YES | MBB_NO, MBT_QUESTION) == MBB_YES)
{
bDone = true;
}
else
{
pPlayer->setPosition(pPlayer->getX(), pPlayer->getY() + 32.0f);
}
g_pState->pScene->resume();
}
}
g_pState->pScene->save(OUTPUTDIR"islandgame_v2.tmx", "", "islandgame.cdc", true);
mC64.sceneMgr().info();
closeTutorial();
return 0;
}
Screen * get(Sint32 idScreen=0)
Get a pointer to the screen using its handle.
Definition CoreConfig.cpp:1497
void setMTFriendly(Uint8 iDelayMS)
Set the multithread-friendly scheduler yield.
Definition CoreConfig.cpp:1183
Sint32 getButtonsPrev() const
Get the previous mouse buttons.
Definition GraphicsCursor.cpp:1053
float getY() const
Get the mouse cursor Y position.
Definition GraphicsCursor.cpp:1041
Sint32 select(Sint32 idCursor)
Select a mouse cursor.
Definition GraphicsCursor.cpp:968
Sint32 getButtons() const
Get the mouse buttons.
Definition GraphicsCursor.cpp:1047
float getX() const
Get the mouse cursor X position.
Definition GraphicsCursor.cpp:1035
Font Object class.
Definition CRM64Pro.h:4741
Sint32 getHeight() const
Get the height of this font (in pixels).
Definition GraphicsFont.cpp:1025
Sint32 render(const string &sText, bool bShowCursor=false, Sint32 iCurPos=-1, Sint32 idRes=0)
Render a text using this font.
Definition GraphicsFont.cpp:1095
bool setPosition(const Position &rPosX, const Position &rPosY, bool bInterpolate=false)
Set the font position.
Definition GraphicsFont.cpp:1037
Sint32 getWidth(const string &sText, Sint32 iCurPos=-1) const
Get the width of the given string (in pixels).
Definition GraphicsFont.cpp:987
Sint32 getBuiltin(const string &sName)
Get a built-in embedded font id using its name.
Definition GraphicsFont.cpp:1436
Sint32 close(Sint32 idFont) override
Close and destroy a font.
Definition GraphicsFont.cpp:1363
Factory interface for creating game objects from Tiled type strings.
Definition CRM64Pro.h:10711
Image Object class.
Definition CRM64Pro.h:3356
SDL_Surface * getSurface()
Get the raw source surface of this image.
Definition GraphicsImage.cpp:1827
Sint32 render(Sint32 idRes=0, SDL_FRect *pSrc=nullptr, SDL_FRect *pDst=nullptr)
Render the image.
Definition GraphicsImage.cpp:1575
Image * get(Sint32 idImg)
Get a pointer to the image using its handle.
Definition GraphicsImage.cpp:2279
Sint32 loadFromFile(const string &sFile, const string &sName="")
Load an image from an external file (BMP, PNG, JPG, PCX, TGA or WebP formats).
Definition GraphicsImage.cpp:2434
Sint32 load(const string &sCDCFile, const string &sName)
Load an image stored in a CDC file.
Definition GraphicsImage.cpp:2476
Sint32 close(Sint32 idImg) override
Close and destroy an image.
Definition GraphicsImage.cpp:2178
Log Object class.
Definition CRM64Pro.h:771
CRM64Pro GDK Main class.
Definition CRM64Pro.h:502
Scene class.
Definition CRM64Pro.h:11200
Object layer containing game objects with spatial partitioning.
Definition CRM64Pro.h:10986
TileLayer class for tile-based map layers.
Definition CRM64Pro.h:10368
Uint32 ** iCellMap
Direct mutable 2D cell map.
Definition CRM64Pro.h:10571
Sint32 loadFromFile(const string &sTMXFile, ISceneObjectFactory *pFactory=nullptr)
Load a level from a TMX file.
Definition SceneMapLoad.cpp:1575
Base class for all Scene game objects.
Definition CRM64Pro.h:10725
Screen Object class.
Definition CRM64Pro.h:1055
Sint32 show()
Show the screen.
Definition CoreConfig.cpp:362
Sint32 setDriver(eConfigRendererDriver eCRD)
Set the renderer driver.
Definition CoreConfig.cpp:454
bool setRenderCallback(RenderCallback fnCallback)
Set a callback function for rendering graphics at Render Frame Rate.
Definition CoreConfig.cpp:765
Sint32 setSize(Sint32 iWidth, Sint32 iHeight)
Set the size of the screen.
Definition CoreConfig.cpp:526
bool setTitle(const string &sName)
Set the title.
Definition CoreConfig.cpp:674
Sprite Object class.
Definition CRM64Pro.h:4262
void setStatsLevel(eMemStatsLevel eMSL)
Set the memory stats gathering level.
Definition CMemoryManager.cpp:618
void destroy()
Releases the internal heap and displays final stats.
Definition CMemoryManager.cpp:954
@ MSL_HIGH
MSL_NORMAL plus deallocations and sizes logging for each module.
Definition CRM64Pro.h:387
eConfigRendererDriver
Screen renderer drivers.
Definition CRM64Pro.h:1013
@ CRD_SOFTWARE
Screen::setDriver(): software renderer driver. Slow but works on all platforms.
Definition CRM64Pro.h:1015
bool msg(eLogLevel eLL, const char *szFmt,...)
Write a message to the log.
Definition CoreLog.cpp:448
Log * get(Sint32 idLog=0)
Get a pointer to the log object.
Definition CoreLog.cpp:921
bool init(const string &sAppname, eLogLevel eLL, eLogMode eLM, const string &sFilename="", const string &sAuthor="", const string &sEmail="", const string &sWeb="")
Initialize the log object.
Definition CoreLog.cpp:338
@ LM_FILE
Log output mode for the 'lm' parameter in CRM64Pro::Log::init(). Output to a file and overwrite exist...
Definition CRM64Pro.h:748
@ LM_STDOUT
Log output mode for the 'lm' parameter in CRM64Pro::Log::init(). Output to standard output (stdout).
Definition CRM64Pro.h:750
@ LL_INFO
Runtime events (startup/shutdown). Messages without timestamp.
Definition CRM64Pro.h:764
@ LL_CRITICAL
Fatal errors, application crash, or assertions.
Definition CRM64Pro.h:761
@ LL_ERROR
Runtime errors that do not require immediate termination.
Definition CRM64Pro.h:762
SpriteMgr & spriteMgr()
Get reference to Sprite Manager.
Definition CoreMain.cpp:772
Timer & timer()
Get reference to Timer module.
Definition CoreMain.cpp:730
Sint32 update(SDL_Event *evUser=nullptr)
Manage the GDK update cycle with a fixed virtual logic frame rate.
Definition CoreMain.cpp:451
ImageMgr & imageMgr()
Get reference to Image Manager.
Definition CoreMain.cpp:754
CursorMgr & cursorMgr()
Get reference to Cursor Manager.
Definition CoreMain.cpp:760
SceneMgr & sceneMgr()
Get reference to Scene Manager.
Definition CoreMain.cpp:814
static void terminate()
Main explicit destructor.
Definition CoreMain.cpp:266
static Main & instance()
Access the Main singleton.
Definition CoreMain.cpp:252
LogMgr & logMgr()
Get reference to Log Manager.
Definition CoreMain.cpp:724
Tool & tool()
Get reference to Tool module.
Definition CoreMain.cpp:718
bool getKeyState(SDL_Keycode keycode) const
Get the current state of a key.
Definition CoreMain.cpp:686
FontMgr & fontMgr()
Get reference to Font Manager.
Definition CoreMain.cpp:778
ConfigMgr & configMgr()
Get reference to Config Manager.
Definition CoreMain.cpp:712
eSceneLayerFeature getLayerFeatures(Sint32 iLayer) const
Get the layer features.
Definition Scene.cpp:810
@ SCM_MANUAL
Manual mode. Camera is controlled only through setLayerPosition()/setCameraPosition().
Definition CRM64Pro.h:10181
@ SCM_SNAP
Instant target tracking each logic tick (no damping, no deadzone behavior).
Definition CRM64Pro.h:10183
@ SLF_RENDER
Enable the rendering feature. Enabling this flag also enables SLF_UPDATE. Disabling render does not d...
Definition CRM64Pro.h:10079
@ SLF_SMOOTHSCROLL
Enable the smooth rendering feature. Check render() for further details.
Definition CRM64Pro.h:10080
@ SLF_UPDATE
Enable the updating feature. Check update() for further details.
Definition CRM64Pro.h:10078
@ SDO_TILE_CELLGRID
Draw tile cell grid overlays for rendered tile layers.
Definition CRM64Pro.h:10097
const string & getType() const
Get the type identifier.
Definition SceneLayerObject.cpp:1505
SceneObject * findFirstByType(const string &sType)
Find first object matching an exact type string.
Definition SceneLayerObject.cpp:3349
const string & getName() const
Get the object name.
Definition SceneLayerObject.cpp:1510
void queryRegion(float fX, float fY, float fW, float fH, vector< SceneObject * > &vResults)
Query objects whose AABB overlaps a region.
Definition SceneLayerObject.cpp:3285
Sint32 getHeight() const
Get map height.
Definition SceneLayerTile.cpp:101
Sint32 getCellWidth() const
Get cell width.
Definition SceneLayerTile.cpp:92
Sint32 getWidth() const
Get map width.
Definition SceneLayerTile.cpp:98
Sint32 close(Sint32 idScene) override
Close and destroy a Scene.
Definition Scene.cpp:2298
bool info(Sint32 iMode=0) override
Request Scene Manager information.
Definition Scene.cpp:2215
Scene * get(Sint32 idScene)
Get a pointer to the Scene using its handle.
Definition Scene.cpp:2355
eGeneralStatus status() const
Get the sprite status.
Definition GraphicsSprite.cpp:749
Sint32 assignImage(Sint32 idImage, Sint32 iOwnership=0)
Assign an Image to this sprite.
Definition GraphicsSprite.cpp:494
Sint32 render(Sint32 idRes=0)
Render the sprite.
Definition GraphicsSprite.cpp:1395
#define SPR_STATE_RIGHT
Definition CRM64Pro.h:4246
bool setAnimStateIndex(Sint32 iIndex, Uint16 iState)
Set the animation state by index.
Definition GraphicsSprite.cpp:927
bool selectAnim(Uint16 iState)
Select an animation by state.
Definition GraphicsSprite.cpp:794
bool pause()
Pause the sprite animation.
Definition GraphicsSprite.cpp:735
#define SPR_STATE_DOWN
Definition CRM64Pro.h:4242
bool setPosition(float fX, float fY, bool bInterpolate=false)
Set the sprite position.
Definition GraphicsSprite.cpp:1356
bool resume()
Resume from a previous paused sprite animation.
Definition GraphicsSprite.cpp:742
Sint32 create(const string &sName, Uint32 iVersion=10) override
Create a new sprite.
Definition GraphicsSprite.cpp:1691
Sint32 getAnimCurrent() const
Get current animation state.
Definition GraphicsSprite.cpp:781
bool setOffset(Sint32 iX, Sint32 iY)
Set the sprite offsets for creating the animations and frames.
Definition GraphicsSprite.cpp:640
#define SPR_STATE_UP
Definition CRM64Pro.h:4240
Sprite * get(Sint32 idSprite)
Get a pointer to the sprite using its handle.
Definition GraphicsSprite.cpp:1843
#define SPR_STATE_LEFT
Definition CRM64Pro.h:4244
bool setFrameTimeIndex(Sint32 iIndex, Sint32 iFrame, Uint16 iTime)
Set the frame time duration by index.
Definition GraphicsSprite.cpp:1284
Sint32 close(Sint32 idSprite) override
Close and destroy a sprite.
Definition GraphicsSprite.cpp:1739
bool init(eTimerState eState=TS_INIT)
Initialize the timer system.
Definition CoreTimer.cpp:543
bool setRate(Uint16 iR, Uint16 iL)
Set preferred Render and Logic Frame Rate.
Definition CoreTimer.cpp:556
eMsgBoxButton messageBox(const string &sTitle, const string &sMsg, eMsgBoxButton eMBB=MBB_OK, eMsgBoxType eMBT=MBT_INFO, Sint32 iTTL=0, Sint32 idCustomPanel=-1, Sint32 idScreen=-1)
Create a message box.
Definition CoreTool.cpp:1080
@ MBB_NO
No button is displayed.
Definition CRM64Pro.h:2513
@ MBB_YES
Yes button is displayed.
Definition CRM64Pro.h:2512
@ MBT_QUESTION
Question icon is displayed.
Definition CRM64Pro.h:2503
CRM64Pro GDK.
Definition AudioTrack.cpp:36
Absolute coordinate or helper-relative position used by layout APIs.
Definition CRM64Pro.h:346
Camera configuration parameters.
Definition CRM64Pro.h:10194
SDL_FPoint ptScreenAnchor
Normalized viewport anchor [0..1]x[0..1] where the target anchor should appear on screen....
Definition CRM64Pro.h:10200
SDL_FPoint ptTargetAnchor
Normalized target anchor [0..1]x[0..1] on the followed object (0,0=top-left, 0.5,1....
Definition CRM64Pro.h:10199
float fDamping
Target-following exponential damping strength in 1/second. Ignored by SCM_AUTOSCROLL....
Definition CRM64Pro.h:10197
bool bClampToBounds
If true, clamp camera to explicit camera bounds (if set) or to layer/world bounds....
Definition CRM64Pro.h:10198
Layer Context structure.
Definition CRM64Pro.h:10223
float fScrollY
Current Logic/Render Y scroll position.
Definition CRM64Pro.h:10228
SDL_Rect rViewport
The effective viewport used for this frame.
Definition CRM64Pro.h:10226
SDL_FPoint worldToScreen(float fWorldX, float fWorldY, bool bQuantize=true) const
Convert world coordinates to screen coordinates using the canonical render camera.
Definition CRM64Pro.h:10273
float fDeltaTime
Time elapsed since last frame (for update). 0 during render.
Definition CRM64Pro.h:10230
float fScrollX
Current Logic/Render X scroll position.
Definition CRM64Pro.h:10227

Navigation

Previous: Tutorial 16.1 - Adventure

Back to the tutorials index

Next: Tutorial 16.3 - Platform