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.1: Scene Adventure

Overview

This first Tutorial 16 example loads Base/tiled/adventure/adventure.tmx. Its image layer draws the world, while the object layer contains the player, a polygon movement area and interactable key and door objects.

Right-click movement stays inside the polygon, left-click interaction checks distance and TMX properties, and the camera follows the player smoothly. A Scene-layer callback renders timed speech while a screen-space panel displays the collected key.

Final result

CRM64Pro Tutorial 16.1 Scene Adventure

Runtime controls

  • Left-click: Interact with or pick up an object.
  • Right-click: Move the player.
  • S: Enable or disable smooth scrolling.
  • D: Show or hide object and trigger debug overlays.
  • Q or ESC: Exit the tutorial.

The debug toggle uses Scene::setDebugObjectOverlay() with ::SDOT_STANDARD, so the object AABB and trigger visualizations remain independent from tile and camera overlays.

Prerequisites

  • CRM64Pro GDK installed and configured with a supported C++17 compiler.
  • Tutorial package downloaded and fully extracted, preserving its folder structure.
  • Run Tutorial 02 first so Tutorial.cdc contains Tutorial_bg.
  • Keep the bin/Base/tiled/adventure TMX, TSX and image assets together.
  • Complete the earlier input, GFX and resource tutorials.

What you will learn

  • Load a TMX map through SceneMgr.
  • Create custom objects with ISceneObjectFactory.
  • Configure layers, callbacks and a target camera.
  • Convert mouse coordinates and query an object layer.
  • Constrain movement to a polygon.
  • Drive interactions from TMX object properties.

Step by step

1. Create tutorial Scene objects

The factory creates one lightweight subclass for every TMX object. Only the player instance runs the custom movement update; inherited rendering needs no override.

class AdventureRenderObject : public SceneObject
{
public:
void update(Scene*, SceneLayerContext& rContext) override
{
if(!g_pState) return;
if(g_pState->pPlayer && getID() == g_pState->pPlayer->getID())
{
updatePlayerMovement(rContext.fDeltaTime);
}
}
};
class TutorialObjectFactory : public ISceneObjectFactory
{
public:
SceneObject* create(const string&) override
{
return new(std::nothrow) AdventureRenderObject();
}
};

2. Load and configure the Scene

Load the TMX with the factory, assign the centered viewport, enable both layers and attach the speech callback to the object layer.

const Sint32 iTE = mC64.sceneMgr().loadFromFile(BASEDIR "tiled/adventure/adventure.tmx", &sTutorialObjectFactory);
g_pState->pScene = mC64.sceneMgr().get(iTE);
if(!g_pState->pScene)
{
if(pLog) pLog->msg(LL_ERROR, "Failed to load adventure map.\n");
return false;
}
SDL_Rect rView = { iViewX, iViewY, iViewW, iViewH };
if(!g_pState->pScene->setViewport(&rView) ||
!g_pState->pScene->setLayerFeatures(1, SLF_UPDATE | SLF_RENDER, true) ||
!g_pState->pScene->setLayerFeatures(2, SLF_UPDATE | SLF_RENDER, true) ||
!g_pState->pScene->setLayerFeatures(1, SLF_SMOOTHSCROLL, true) ||
!g_pState->pScene->setLayerFeatures(2, SLF_SMOOTHSCROLL, true) ||
!g_pState->pScene->setLayerOnRenderEnd(2, onLayerRenderSpeech))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to configure adventure Scene layers.\n");
return false;
}

3. Resolve the required TMX objects

Find the player and optional movement area by type or name. The player is required; without area_player, movement remains unrestricted.

if((pObj->getType() == "player" || pObj->getName() == "player") && !g_pState->pPlayer)
{
g_pState->pPlayer = pObj;
g_pState->fTargetX = pObj->getX() + (pObj->getWidth() * 0.5f);
g_pState->fTargetY = pObj->getY() + pObj->getHeight();
}
else if((pObj->getType() == "area_player" || pObj->getName() == "area_player") && !g_pState->pAreaPlayer)
{
g_pState->pAreaPlayer = pObj;
}
if(!g_pState->pPlayer)
{
if(pLog) pLog->msg(LL_ERROR, "Player object not found (name/type: player).\n");
return false;
}
if(!g_pState->pAreaPlayer && pLog) pLog->msg(LL_WARNING, "area_player object not found.\n");

4. Follow the player with a smooth camera

Use the player's feet as the target anchor, center that point in the viewport and clamp camera movement to Scene bounds.

SceneCameraParams mCam;
mCam.fDamping = 10.0f;
mCam.bClampToBounds = true;
mCam.ptTargetAnchor = { 0.5f, 1.0f };
mCam.ptScreenAnchor = { 0.5f, 0.5f };
if(!g_pState->pScene->setCameraTarget(2, g_pState->pPlayer) ||
!g_pState->pScene->setCameraParams(2, mCam) ||
!g_pState->pScene->setCameraMode(2, SCM_SMOOTH))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to configure adventure camera.\n");
return false;
}

5. Query objects under the pointer

Convert the mouse from screen to layer-world coordinates, query the spatial region and keep only shapes that contain the exact point.

const bool bCursorWorldValid = (g_pState->pScene && g_pState->pObjLayer &&
g_pState->pScene->mouseToWorld(&fWX, &fWY, 2));
if(bCursorWorldValid)
{
vector<SceneObject*> vCandidates;
g_pState->pObjLayer->queryRegion(fWX, fWY, 1.0f, 1.0f, vCandidates);
for(SceneObject* pObj : vCandidates)
{
if(pObj && pObj->containsPoint(fWX, fWY))
{
vHits.push_back(pObj);
}
}
}

6. Constrain movement to the polygon

Accept a point inside the area, try axis-aligned sliding, then fall back to the closest polygon boundary point.

// Keep a hotspot inside the player area.
static bool constrainHotspotToPlayerArea(float oldHotX, float oldHotY, float newHotX, float newHotY, float& rOutHotX, float& rOutHotY)
{
if(isInsidePlayerArea(newHotX, newHotY))
{
rOutHotX = newHotX;
rOutHotY = newHotY;
return true;
}
if(isInsidePlayerArea(newHotX, oldHotY))
{
rOutHotX = newHotX;
rOutHotY = oldHotY;
return true;
}
if(isInsidePlayerArea(oldHotX, newHotY))
{
rOutHotX = oldHotX;
rOutHotY = newHotY;
return true;
}
float edgeX = oldHotX;
float edgeY = oldHotY;
if(nearestPointOnAreaBoundary(newHotX, newHotY, edgeX, edgeY))
{
rOutHotX = edgeX;
rOutHotY = edgeY;
return true;
}
return false;
}

7. Choose a right-click destination

Use the world point directly when it is inside the area, otherwise project it to the nearest boundary before starting movement.

if(g_pState->pAreaPlayer)
{
bValidTarget = g_pState->pAreaPlayer->containsPoint(fTX, fTY);
if(!bValidTarget)
{
bValidTarget = nearestPointOnAreaBoundary(fTX, fTY, fMoveTX, fMoveTY);
}
}
if(bValidTarget)
{
g_pState->fTargetX = fMoveTX;
g_pState->fTargetY = fMoveTY;
g_pState->bMoving = true;
}
else if(pLog) pLog->msg(LL_INFO, "Target out of area_player: (%.1f, %.1f)\n", fTX, fTY);

8. Read interaction properties

Require proximity, then read the TMX pickup and keydoor properties to select the gameplay action.

if(!isPlayerNearObject(pObj, fInteractDistance))
{
setSpeech("I need to get closer.");
continue;
}
const bool bPickup = pObj->getPropertyBool("pickup", false);
const Sint32 iKeyDoor = pObj->getPropertyInt("keydoor", -1);

9. Pick up the key safely

Copy the borrowed object's identity and remove it first. Inventory changes only after closeObject() succeeds.

if(bPickup)
{
const Sint32 idObject = pObj->getID();
const string sObjectName = pObj->getName();
if(!g_pState->pObjLayer->closeObject(idObject))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to pick up '%s'.\n", sObjectName.c_str());
setSpeech("I couldn't pick that up.");
continue;
}
if(iKeyDoor >= 0)
{
g_pState->iCollectedKeyDoor = iKeyDoor;
g_pState->bHasKeyInInventory = true;
}
if(pLog) pLog->msg(LL_INFO, "Picked up '%s' (keydoor=%d)\n", sObjectName.c_str(), iKeyDoor);
setSpeech("You got the key.");
continue;
}

10. Unlock the matching door

A matching keydoor value removes the door, clears the inventory slot and ends the example. Failed removal leaves gameplay state untouched.

if(g_pState->iCollectedKeyDoor == iKeyDoor)
{
const Sint32 idObject = pObj->getID();
const string sObjectName = pObj->getName();
if(!g_pState->pObjLayer->closeObject(idObject))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to unlock '%s'.\n", sObjectName.c_str());
setSpeech("The door would not open.");
continue;
}
if(pLog) pLog->msg(LL_INFO, "Unlocked '%s' with keydoor=%d\n", sObjectName.c_str(), iKeyDoor);
g_pState->bHasKeyInInventory = false;
if(!g_pState->bGameFinished)
{
g_pState->bGameFinished = true;
g_pState->pScene->pause();
mC64.tool().messageBox("Adventure", "You unlocked the door. End of game.", MBB_OK, MBT_INFO);
g_pState->pScene->resume();
bDone = true;
break;
}
}

11. Render speech in layer coordinates

The layer callback converts a point above the player into screen coordinates and uses the font loaded during setup.

if(g_pState->pPlayer)
{
SDL_FPoint pScreen = rContext.worldToScreen(
g_pState->pPlayer->getX() + (g_pState->pPlayer->getWidth() * 0.5f) + 16.0f,
g_pState->pPlayer->getY() - 20.0f, true);
fTextX = pScreen.x;
fTextY = pScreen.y;
}
if(fTextX < static_cast<float>(rContext.rViewport.x)) fTextX = static_cast<float>(rContext.rViewport.x);
if(fTextY < static_cast<float>(rContext.rViewport.y)) fTextY = static_cast<float>(rContext.rViewport.y);
pFont->setPosition(fTextX, fTextY);
pFont->render(g_pState->sSpeech.c_str());

12. Update and release in ownership order

Scene::update() advances objects and the camera. A shared cleanup function also handles partially completed setup, closing the Scene before the font and image resources it uses.

if(bDone) break;
g_pState->pScene->update();
static void closeTutorial()
{
Main& mC64 = Main::instance();
mC64.sceneMgr().close(0);
mC64.fontMgr().close(0);
mC64.imageMgr().close(0);
Main::terminate();
}
mC64.sceneMgr().info();
closeTutorial();
return 0;

Complete source

The tutorial source is authoritative. The following listing is included directly from validation/source/Tutorial_16_Scene_Adventure.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 Adventure (Point&Click Basics)
------------------------------------------------------------------------
Overview:
This tutorial loads a point-and-click adventure map, moves the player with
the mouse, picks up a key, and opens a matching door.
Key concepts:
- Load a TMX map with SceneMgr.
- Use TMX object properties for gameplay data.
- Query an object layer from the mouse position.
- Move a SceneObject inside a polygon area.
- Use a Screen render callback to draw the Scene.
Layer layout:
- Layer 1: Image world background.
- Layer 2: Object layer (player, area_player, interactables).
Controls:
- Left-Click: Interact / pick up.
- Right-Click: Move player.
- S: Toggle smooth scroll.
- D: Toggle object debug AABB and trigger zones.
- Q / ESC: Quit.
Loop order:
- Poll events and capture mouse button edges.
- Convert the mouse position to world coordinates.
- Resolve hover, interaction and movement targets.
- Run one fixed logic tick via Scene::update().
- Let Scene update the camera.
- Render from callback using Scene::render().
------------------------------------------------------------------------ */
#include "CRM64Pro.h"
#include "RunParameter.h"
using namespace CRM64Pro;
#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 = 600;
static const Sint32 iViewH = 500;
static const Sint32 iViewX = (iScreenW - iViewW) / 2;
static const Sint32 iViewY = 0;
// Logic timing.
static const Sint32 iLogicRate = 20;
static const float fPlayerSpeed = 80.0f;
static const float fInteractDistance = 20.0f;
static const Uint32 iSpeechDurationMS = 1700;
struct TutorialState
{
Scene* pScene = nullptr;
Sint32 idBgImage = -1;
Sint32 idGuiImage = -1;
Sint32 idKeyImage = -1;
Sint32 idBuiltInFont = -1;
string sSpeech;
Uint64 iSpeechUntil = 0;
SceneLayerObject* pObjLayer = nullptr;
SceneObject* pPlayer = nullptr;
SceneObject* pAreaPlayer = nullptr;
float fTargetX = 0.0f;
float fTargetY = 0.0f;
bool bMoving = false;
Sint32 iCollectedKeyDoor = -1;
bool bHasKeyInInventory = false;
bool bGameFinished = false;
};
static TutorialState* g_pState = nullptr;
static void updatePlayerMovement(float fDeltaTime);
static void onLayerRenderSpeech(const SceneLayerContext& rContext);
class AdventureRenderObject : public SceneObject
{
public:
void update(Scene*, SceneLayerContext& rContext) override
{
if(!g_pState) return;
if(g_pState->pPlayer && getID() == g_pState->pPlayer->getID())
{
updatePlayerMovement(rContext.fDeltaTime);
}
}
};
class TutorialObjectFactory : public ISceneObjectFactory
{
public:
SceneObject* create(const string&) override
{
return new(std::nothrow) AdventureRenderObject();
}
};
// Show a speech message for the requested duration.
static void setSpeech(const string& sText, Uint32 iDurationMS = iSpeechDurationMS)
{
g_pState->sSpeech = sText;
g_pState->iSpeechUntil = Main::instance().timer().getTicks() + static_cast<Uint64>(iDurationMS);
}
// Check whether a hotspot is inside the player area.
static bool isInsidePlayerArea(float fHotX, float fHotY)
{
if(!g_pState) return true;
if(!g_pState->pAreaPlayer) return true;
return g_pState->pAreaPlayer->containsPoint(fHotX, fHotY);
}
// Find the closest point on the player area boundary.
static bool nearestPointOnAreaBoundary(float px, float py, float& rOutX, float& rOutY)
{
if(!g_pState) return false;
if(!g_pState->pAreaPlayer) return false;
const SceneShapeData& shape = g_pState->pAreaPlayer->getShape();
if(shape.eType != SST_POLYGON || shape.vPoints.size() < 2) return false;
const float baseX = g_pState->pAreaPlayer->getX();
const float baseY = g_pState->pAreaPlayer->getY();
float bestDist2 = 1.0e30f;
float bestX = px;
float bestY = py;
const size_t n = shape.vPoints.size();
for(size_t i = 0; i < n; ++i)
{
const SDL_FPoint& a = shape.vPoints[i];
const SDL_FPoint& b = shape.vPoints[(i + 1) % n];
const float ax = baseX + a.x;
const float ay = baseY + a.y;
const float bx = baseX + b.x;
const float by = baseY + b.y;
const float abx = bx - ax;
const float aby = by - ay;
const float apx = px - ax;
const float apy = py - ay;
const float abLen2 = (abx * abx) + (aby * aby);
float t = 0.0f;
if(abLen2 > 0.00001f)
{
t = ((apx * abx) + (apy * aby)) / abLen2;
if(t < 0.0f) t = 0.0f;
if(t > 1.0f) t = 1.0f;
}
const float qx = ax + (abx * t);
const float qy = ay + (aby * t);
const float dx = px - qx;
const float dy = py - qy;
const float d2 = (dx * dx) + (dy * dy);
if(d2 < bestDist2)
{
bestDist2 = d2;
bestX = qx;
bestY = qy;
}
}
rOutX = bestX;
rOutY = bestY;
return true;
}
// Keep a hotspot inside the player area.
static bool constrainHotspotToPlayerArea(float oldHotX, float oldHotY, float newHotX, float newHotY, float& rOutHotX, float& rOutHotY)
{
if(isInsidePlayerArea(newHotX, newHotY))
{
rOutHotX = newHotX;
rOutHotY = newHotY;
return true;
}
if(isInsidePlayerArea(newHotX, oldHotY))
{
rOutHotX = newHotX;
rOutHotY = oldHotY;
return true;
}
if(isInsidePlayerArea(oldHotX, newHotY))
{
rOutHotX = oldHotX;
rOutHotY = newHotY;
return true;
}
float edgeX = oldHotX;
float edgeY = oldHotY;
if(nearestPointOnAreaBoundary(newHotX, newHotY, edgeX, edgeY))
{
rOutHotX = edgeX;
rOutHotY = edgeY;
return true;
}
return false;
}
// Check whether the player is close enough to an object.
static bool isPlayerNearObject(const SceneObject* pObj, float fMaxDistance)
{
if(!g_pState) return false;
if(!pObj || !g_pState->pPlayer) return false;
// Player proximity uses full player box (not a single hotspot point).
const float pX = g_pState->pPlayer->getX();
const float pY = g_pState->pPlayer->getY();
const float pW = g_pState->pPlayer->getWidth();
const float pH = g_pState->pPlayer->getHeight();
const float pMinX = pX;
const float pMinY = pY;
const float pMaxX = pX + pW;
const float pMaxY = pY + pH;
const Physics::AABB& aabb = pObj->getShape().AABB;
const float oMinX = aabb.minX - fMaxDistance;
const float oMinY = aabb.minY - fMaxDistance;
const float oMaxX = aabb.maxX + fMaxDistance;
const float oMaxY = aabb.maxY + fMaxDistance;
const bool bOverlapX = (pMaxX >= oMinX) && (pMinX <= oMaxX);
const bool bOverlapY = (pMaxY >= oMinY) && (pMinY <= oMaxY);
return bOverlapX && bOverlapY;
}
// Adapt the renderer to the engine callback signature.
static Sint32 renderWrapper(Sint32)
{
if(!g_pState || !g_pState->pScene) return 0;
if(g_pState->idBgImage > 0)
{
Image* pBG = Main::instance().imageMgr().get(g_pState->idBgImage);
if(pBG) pBG->render();
}
g_pState->pScene->render();
// UI overlay (screen space): centered in the strip below the viewport.
Image* pGUI = (g_pState->idGuiImage > 0) ? Main::instance().imageMgr().get(g_pState->idGuiImage) : nullptr;
if(pGUI)
{
const float fGuiW = static_cast<float>(pGUI->getWidth());
const float fGuiH = static_cast<float>(pGUI->getHeight());
const float fGuiX = (static_cast<float>(iScreenW) - fGuiW) * 0.5f;
const float fGuiAreaY = static_cast<float>(iViewY + iViewH);
const float fGuiAreaH = static_cast<float>(iScreenH - (iViewY + iViewH));
const float fGuiY = fGuiAreaY + ((fGuiAreaH - fGuiH) * 0.5f);
SDL_FRect rGuiDst = { fGuiX, fGuiY, fGuiW, fGuiH };
pGUI->render(0, nullptr, &rGuiDst);
if(g_pState->bHasKeyInInventory && g_pState->idKeyImage > 0)
{
Image* pKey = Main::instance().imageMgr().get(g_pState->idKeyImage);
if(pKey)
{
const float fKeyW = static_cast<float>(pKey->getWidth());
const float fKeyH = static_cast<float>(pKey->getHeight());
// Fixed inventory slot over gui.png (left-side slot).
const float fKeyX = fGuiX + 10.0f;
const float fKeyY = fGuiY + ((fGuiH - fKeyH) * 0.5f);
SDL_FRect rKeyDst = { fKeyX, fKeyY, fKeyW, fKeyH };
pKey->render(0, nullptr, &rKeyDst);
}
}
}
return 0;
}
// Draw the active speech message on the scene layer.
static void onLayerRenderSpeech(const SceneLayerContext& rContext)
{
if(rContext.idLayer != 2) return;
if(g_pState->sSpeech.empty()) return;
if(Main::instance().timer().getTicks() > g_pState->iSpeechUntil) return;
Font* pFont = Main::instance().fontMgr().get(g_pState->idBuiltInFont);
if(!pFont) return;
float fTextX = static_cast<float>(rContext.rViewport.x + 14);
float fTextY = static_cast<float>(rContext.rViewport.y + 14);
if(g_pState->pPlayer)
{
SDL_FPoint pScreen = rContext.worldToScreen(
g_pState->pPlayer->getX() + (g_pState->pPlayer->getWidth() * 0.5f) + 16.0f,
g_pState->pPlayer->getY() - 20.0f, true);
fTextX = pScreen.x;
fTextY = pScreen.y;
}
if(fTextX < static_cast<float>(rContext.rViewport.x)) fTextX = static_cast<float>(rContext.rViewport.x);
if(fTextY < static_cast<float>(rContext.rViewport.y)) fTextY = static_cast<float>(rContext.rViewport.y);
pFont->setPosition(fTextX, fTextY);
pFont->render(g_pState->sSpeech.c_str());
}
// Load the map and find the objects used by the tutorial.
static bool loadMapAndResolveObjects()
{
if(!g_pState) return false;
Main& mC64 = Main::instance();
Log* pLog = mC64.logMgr().get();
static TutorialObjectFactory sTutorialObjectFactory;
const Sint32 iTE = mC64.sceneMgr().loadFromFile(BASEDIR "tiled/adventure/adventure.tmx", &sTutorialObjectFactory);
g_pState->pScene = mC64.sceneMgr().get(iTE);
if(!g_pState->pScene)
{
if(pLog) pLog->msg(LL_ERROR, "Failed to load adventure map.\n");
return false;
}
SDL_Rect rView = { iViewX, iViewY, iViewW, iViewH };
if(!g_pState->pScene->setViewport(&rView) ||
!g_pState->pScene->setLayerFeatures(1, SLF_UPDATE | SLF_RENDER, true) ||
!g_pState->pScene->setLayerFeatures(2, SLF_UPDATE | SLF_RENDER, true) ||
!g_pState->pScene->setLayerFeatures(1, SLF_SMOOTHSCROLL, true) ||
!g_pState->pScene->setLayerFeatures(2, SLF_SMOOTHSCROLL, true) ||
!g_pState->pScene->setLayerOnRenderEnd(2, onLayerRenderSpeech))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to configure adventure Scene layers.\n");
return false;
}
if(pLog)
{
SceneLayerImage* pL1 = g_pState->pScene->accessLayerImage(1);
SceneLayerObject* pL2 = g_pState->pScene->accessLayerObject(2);
const float fL1W = (pL1 ? pL1->getTotalWidth() : -1.0f);
const float fL1H = (pL1 ? pL1->getTotalHeight() : -1.0f);
const float fL2W = (pL2 ? pL2->getTotalWidth() : -1.0f);
const float fL2H = (pL2 ? pL2->getTotalHeight() : -1.0f);
pLog->msg(LL_INFO, "Adventure: layer 1 image size: %.1fx%.1f | layer 2 object size: %.1fx%.1f | Scene size: %dx%d\n",
fL1W, fL1H, fL2W, fL2H, g_pState->pScene->getWidth(), g_pState->pScene->getHeight());
}
g_pState->pObjLayer = g_pState->pScene->accessLayerObject(2);
if(!g_pState->pObjLayer)
{
if(pLog) pLog->msg(LL_ERROR, "Object layer not found at index 2.\n");
return false;
}
g_pState->pObjLayer->setRenderOrderMode(SORO_FEET);
vector<SceneObject*> vObjects;
g_pState->pObjLayer->queryRegion(0.0f, 0.0f, 100000.0f, 100000.0f, vObjects);
for(SceneObject* pObj : vObjects)
{
if(pLog)
{
pLog->msg(LL_INFO, "Adventure object loaded: id=%d name='%s' type='%s'\n",
pObj->getID(), pObj->getName().c_str(), pObj->getType().c_str());
}
const string& rType = pObj->getType();
if(rType.empty() ||
(rType != "player" &&
rType != "area_player" &&
rType != "interact"))
{
if(pLog)
{
pLog->msg(LL_WARNING,
"Unsupported adventure object type: id=%d name='%s' type='%s'\n",
pObj->getID(), pObj->getName().c_str(), pObj->getType().c_str());
}
}
if((pObj->getType() == "player" || pObj->getName() == "player") && !g_pState->pPlayer)
{
g_pState->pPlayer = pObj;
g_pState->fTargetX = pObj->getX() + (pObj->getWidth() * 0.5f);
g_pState->fTargetY = pObj->getY() + pObj->getHeight();
}
else if((pObj->getType() == "area_player" || pObj->getName() == "area_player") && !g_pState->pAreaPlayer)
{
g_pState->pAreaPlayer = pObj;
}
}
if(!g_pState->pPlayer)
{
if(pLog) pLog->msg(LL_ERROR, "Player object not found (name/type: player).\n");
return false;
}
if(!g_pState->pAreaPlayer && pLog) pLog->msg(LL_WARNING, "area_player object not found.\n");
mCam.fDamping = 10.0f;
mCam.bClampToBounds = true;
mCam.ptTargetAnchor = { 0.5f, 1.0f };
mCam.ptScreenAnchor = { 0.5f, 0.5f };
if(!g_pState->pScene->setCameraTarget(2, g_pState->pPlayer) ||
!g_pState->pScene->setCameraParams(2, mCam) ||
!g_pState->pScene->setCameraMode(2, SCM_SMOOTH))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to configure adventure camera.\n");
return false;
}
return true;
}
// Move the player according to the current input.
static void updatePlayerMovement(float fDeltaTime)
{
if(!g_pState) return;
if(!g_pState->bMoving || !g_pState->pPlayer) return;
const float fHotX = g_pState->pPlayer->getX() + (g_pState->pPlayer->getWidth() * 0.5f);
const float fHotY = g_pState->pPlayer->getY() + g_pState->pPlayer->getHeight();
const float fDX = g_pState->fTargetX - fHotX;
const float fDY = g_pState->fTargetY - fHotY;
const float fDist = sqrtf(fDX * fDX + fDY * fDY);
const float fStep = fPlayerSpeed * fDeltaTime;
if(fDist <= fStep || fDist <= 0.001f)
{
float fOutHotX = fHotX;
float fOutHotY = fHotY;
if(constrainHotspotToPlayerArea(fHotX, fHotY, g_pState->fTargetX, g_pState->fTargetY, fOutHotX, fOutHotY))
{
g_pState->pPlayer->setPosition(fOutHotX - (g_pState->pPlayer->getWidth() * 0.5f), fOutHotY - g_pState->pPlayer->getHeight());
}
g_pState->bMoving = false;
return;
}
const float fInvDist = 1.0f / fDist;
const float fNewHotX = fHotX + (fDX * fInvDist * fStep);
const float fNewHotY = fHotY + (fDY * fInvDist * fStep);
float fOutHotX = fHotX;
float fOutHotY = fHotY;
if(!constrainHotspotToPlayerArea(fHotX, fHotY, fNewHotX, fNewHotY, fOutHotX, fOutHotY))
{
g_pState->bMoving = false;
return;
}
g_pState->pPlayer->setPosition(fOutHotX - (g_pState->pPlayer->getWidth() * 0.5f), fOutHotY - g_pState->pPlayer->getHeight());
}
// Close resources in ownership order, including partially completed setup.
static void closeTutorial()
{
Main& mC64 = Main::instance();
mC64.sceneMgr().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;
atexit(CMem::destroy);
Main& mC64 = Main::instance();
TutorialState state;
g_pState = &state;
Log* pLog = mC64.logMgr().get();
pLog->init("Tutorial_16_Scene_Adventure", LL_DEBUG, LM_FILE | LM_STDOUT, OUTPUTDIR"Tutorial_16_Scene_Adventure.log");
if(!mC64.timer().init() || !mC64.timer().setRate(0, iLogicRate))
{
if(pLog) pLog->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 - Adventure");
if(pScreen->show() < 0)
{
return -1;
}
if(!pScreen->setRenderCallback(renderWrapper))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to set the render callback.\n");
return -1;
}
g_pState->idBgImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_BG_CDC_NAME);
g_pState->idGuiImage = mC64.imageMgr().loadFromFile(BASEDIR "tiled/adventure/gui.png");
g_pState->idKeyImage = mC64.imageMgr().loadFromFile(BASEDIR "tiled/adventure/key.png", "keyGUI");
g_pState->idBuiltInFont = mC64.fontMgr().getBuiltin("CourierNew10White");
if(g_pState->idBgImage <= 0 || g_pState->idGuiImage <= 0 || g_pState->idKeyImage <= 0 ||
g_pState->idBuiltInFont <= 0 || !mC64.fontMgr().get(g_pState->idBuiltInFont))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to load adventure background, UI or font resources.\n");
closeTutorial();
return -1;
}
if(!loadMapAndResolveObjects())
{
closeTutorial();
return -1;
}
if(pLog)
{
pLog->msg(LL_INFO, "Adventure tutorial loaded.\n");
pLog->msg(LL_INFO, "\nCONTROLS:\n [Left-Click] Interact with objects\n [Right-Click] Move\n [S] Toggle smooth scroll\n [D] Toggle object AABB\n [Q/Esc] Quit\n\n");
}
SDL_Event ev;
bool bDone = false;
bool bSmooth = true;
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_S && g_pState->pScene)
{
bSmooth = !bSmooth;
g_pState->pScene->setLayerFeatures(1, SLF_SMOOTHSCROLL, bSmooth);
g_pState->pScene->setLayerFeatures(2, SLF_SMOOTHSCROLL, bSmooth);
if(pLog) pLog->msg(LL_INFO, "Smooth scroll: %s\n", bSmooth ? "ON" : "OFF");
}
if(ev.key.key == SDLK_D && g_pState->pObjLayer)
{
const bool bEnabled = !g_pState->pScene->isDebugObjectOverlayEnabled(SDO_OBJECT_AABB, SDOT_STANDARD);
g_pState->pScene->setDebugObjectOverlay(SDO_OBJECT_AABB, SDOT_STANDARD, bEnabled);
g_pState->pScene->setDebugObjectOverlay(SDO_TRIGGER_ZONES, SDOT_STANDARD, bEnabled);
if(pLog) pLog->msg(LL_INFO, "Debug AABB: %s\n", bEnabled ? "ON" : "OFF");
}
}
}
if(bDone) break;
const Sint32 iMouseButtons = mC64.cursorMgr().getButtons();
const Sint32 iMouseButtonsPrev = mC64.cursorMgr().getButtonsPrev();
const bool bLeftPressed =
((iMouseButtons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) != 0) &&
((iMouseButtonsPrev & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) == 0);
const bool bRightPressed =
((iMouseButtons & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) != 0) &&
((iMouseButtonsPrev & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) == 0);
float fWX = 0.0f;
float fWY = 0.0f;
vector<SceneObject*> vHits;
const bool bCursorWorldValid = (g_pState->pScene && g_pState->pObjLayer &&
g_pState->pScene->mouseToWorld(&fWX, &fWY, 2));
if(bCursorWorldValid)
{
vector<SceneObject*> vCandidates;
g_pState->pObjLayer->queryRegion(fWX, fWY, 1.0f, 1.0f, vCandidates);
for(SceneObject* pObj : vCandidates)
{
if(pObj && pObj->containsPoint(fWX, fWY))
{
vHits.push_back(pObj);
}
}
}
bool bHoverInteract = false;
for(SceneObject* pObj : vHits)
{
if(pObj->getType() == "interact")
{
bHoverInteract = true;
break;
}
}
mC64.cursorMgr().select(bHoverInteract ? SDL_SYSTEM_CURSOR_POINTER : SDL_SYSTEM_CURSOR_DEFAULT);
if(bLeftPressed)
{
for(SceneObject* pObj : vHits)
{
if(!pObj || pObj->getType() != "interact") continue;
if(!isPlayerNearObject(pObj, fInteractDistance))
{
setSpeech("I need to get closer.");
continue;
}
const bool bPickup = pObj->getPropertyBool("pickup", false);
const Sint32 iKeyDoor = pObj->getPropertyInt("keydoor", -1);
if(bPickup)
{
const Sint32 idObject = pObj->getID();
const string sObjectName = pObj->getName();
if(!g_pState->pObjLayer->closeObject(idObject))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to pick up '%s'.\n", sObjectName.c_str());
setSpeech("I couldn't pick that up.");
continue;
}
if(iKeyDoor >= 0)
{
g_pState->iCollectedKeyDoor = iKeyDoor;
g_pState->bHasKeyInInventory = true;
}
if(pLog) pLog->msg(LL_INFO, "Picked up '%s' (keydoor=%d)\n", sObjectName.c_str(), iKeyDoor);
setSpeech("You got the key.");
continue;
}
if(iKeyDoor >= 0)
{
if(g_pState->iCollectedKeyDoor == iKeyDoor)
{
const Sint32 idObject = pObj->getID();
const string sObjectName = pObj->getName();
if(!g_pState->pObjLayer->closeObject(idObject))
{
if(pLog) pLog->msg(LL_ERROR, "Failed to unlock '%s'.\n", sObjectName.c_str());
setSpeech("The door would not open.");
continue;
}
if(pLog) pLog->msg(LL_INFO, "Unlocked '%s' with keydoor=%d\n", sObjectName.c_str(), iKeyDoor);
g_pState->bHasKeyInInventory = false;
if(!g_pState->bGameFinished)
{
g_pState->bGameFinished = true;
g_pState->pScene->pause();
mC64.tool().messageBox("Adventure", "You unlocked the door. End of game.", MBB_OK, MBT_INFO);
g_pState->pScene->resume();
bDone = true;
break;
}
}
else
{
if(pLog) pLog->msg(LL_INFO, "'%s' is locked. Missing keydoor=%d\n", pObj->getName().c_str(), iKeyDoor);
setSpeech("It's locked.");
}
}
}
}
if(bRightPressed && g_pState->pPlayer && bCursorWorldValid)
{
const float fTX = fWX;
const float fTY = fWY;
bool bValidTarget = true;
float fMoveTX = fTX;
float fMoveTY = fTY;
if(g_pState->pAreaPlayer)
{
bValidTarget = g_pState->pAreaPlayer->containsPoint(fTX, fTY);
if(!bValidTarget)
{
bValidTarget = nearestPointOnAreaBoundary(fTX, fTY, fMoveTX, fMoveTY);
}
}
if(bValidTarget)
{
g_pState->fTargetX = fMoveTX;
g_pState->fTargetY = fMoveTY;
g_pState->bMoving = true;
}
else if(pLog) pLog->msg(LL_INFO, "Target out of area_player: (%.1f, %.1f)\n", fTX, fTY);
}
if(bDone) break;
g_pState->pScene->update();
}
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
Sint32 select(Sint32 idCursor)
Select a mouse cursor.
Definition GraphicsCursor.cpp:968
Sint32 getButtons() const
Get the mouse buttons.
Definition GraphicsCursor.cpp:1047
Font Object class.
Definition CRM64Pro.h:4741
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 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
Font * get(const string &sName)
Get a pointer to the font using its name.
Definition GraphicsFont.cpp:1235
Factory interface for creating game objects from Tiled type strings.
Definition CRM64Pro.h:10711
Image Object class.
Definition CRM64Pro.h:3356
Sint32 getHeight() const
Get the image height.
Definition GraphicsImage.cpp:1839
Sint32 getWidth() const
Get the image width.
Definition GraphicsImage.cpp:1833
Sint32 render(Sint32 idRes=0, SDL_FRect *pSrc=nullptr, SDL_FRect *pDst=nullptr)
Render the image.
Definition GraphicsImage.cpp:1575
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
Image layer containing a single image for background/foreground display.
Definition CRM64Pro.h:11130
Object layer containing game objects with spatial partitioning.
Definition CRM64Pro.h:10986
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
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_WARNING
Undesirable states or use of deprecated APIs.
Definition CRM64Pro.h:763
@ LL_INFO
Runtime events (startup/shutdown). Messages without timestamp.
Definition CRM64Pro.h:764
@ LL_DEBUG
Detailed information for flow control.
Definition CRM64Pro.h:765
@ LL_ERROR
Runtime errors that do not require immediate termination.
Definition CRM64Pro.h:762
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
FontMgr & fontMgr()
Get reference to Font Manager.
Definition CoreMain.cpp:778
ConfigMgr & configMgr()
Get reference to Config Manager.
Definition CoreMain.cpp:712
@ SCM_SMOOTH
Damped target tracking using SceneCameraParams::fDamping.
Definition CRM64Pro.h:10184
@ SORO_FEET
Draw by bottom Y (y + height).
Definition CRM64Pro.h:10162
@ SST_POLYGON
Closed polygon shape defined by vertices.
Definition CRM64Pro.h:10142
@ 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_OBJECT_AABB
Draw object AABBs for selected object types.
Definition CRM64Pro.h:10093
@ SDO_TRIGGER_ZONES
Draw trigger zone bounds for selected object types.
Definition CRM64Pro.h:10096
@ SDOT_STANDARD
Standard and user-defined object types.
Definition CRM64Pro.h:10109
float getTotalWidth() const override
Get layer total width in pixels.
Definition SceneLayerImage.cpp:607
float getTotalHeight() const override
Get layer total height in pixels.
Definition SceneLayerImage.cpp:614
Sint32 getPropertyInt(const string &sKey, Sint32 iDefault=0) const
Get an integer custom property.
Definition SceneLayerObject.cpp:1654
Sint32 getID() const
Get the object ID.
Definition SceneLayerObject.cpp:1499
const string & getType() const
Get the type identifier.
Definition SceneLayerObject.cpp:1505
float getHeight() const
Get the object height.
Definition SceneLayerObject.cpp:1475
float getTotalWidth() const override
Get layer total width in pixels.
Definition SceneLayerObject.cpp:3440
bool containsPoint(float fX, float fY) const
Test if a point is inside this object's shape.
Definition SceneLayerObject.cpp:1765
float getTotalHeight() const override
Get layer total height in pixels.
Definition SceneLayerObject.cpp:3446
float getWidth() const
Get the object width.
Definition SceneLayerObject.cpp:1469
float getY() const
Get the object Y position.
Definition SceneLayerObject.cpp:1463
bool getPropertyBool(const string &sKey, bool bDefault=false) const
Get a boolean custom property.
Definition SceneLayerObject.cpp:1662
float getX() const
Get the object X position.
Definition SceneLayerObject.cpp:1457
const string & getName() const
Get the object name.
Definition SceneLayerObject.cpp:1510
const SceneShapeData & getShape() const
Get the object shape data.
Definition SceneLayerObject.cpp:1523
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
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_OK
OK button is displayed.
Definition CRM64Pro.h:2510
@ MBT_INFO
Information icon is displayed.
Definition CRM64Pro.h:2502
CRM64Pro GDK.
Definition AudioTrack.cpp:36
Axis-aligned bounding box using half-open bounds [min, max).
Definition CRM64Pro.h:5834
float minY
Inclusive lower bound on the Y axis.
Definition CRM64Pro.h:5836
float maxX
Exclusive upper bound on the X axis.
Definition CRM64Pro.h:5837
float maxY
Exclusive upper bound on the Y axis.
Definition CRM64Pro.h:5838
float minX
Inclusive lower bound on the X axis.
Definition CRM64Pro.h:5835
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
Sint32 idLayer
The ID of the layer.
Definition CRM64Pro.h:10224
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
Shape geometry data for a SceneObject.
Definition CRM64Pro.h:10602
vector< SDL_FPoint > vPoints
Polygon/polyline vertices relative to object (x,y). Empty for other shapes.
Definition CRM64Pro.h:10604
eSceneShapeType eType
Shape type (rectangle, ellipse, polygon, etc.).
Definition CRM64Pro.h:10603
Physics::AABB AABB
Cached bounding box in world space.
Definition CRM64Pro.h:10605

Navigation

Previous: Tutorial 15 - Network

Back to the tutorials index

Next: Tutorial 16.2 - Island Game v2