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.4: Scene Shooter

Overview

This tutorial builds a vertically scrolling shooter with TMX-created entities, player and enemy projectiles, broad- and narrow-phase collision, explosions, camera shake and a HUD.

Final result

CRM64Pro Tutorial 16.4 Scene Shooter

Runtime controls

  • Cursor keys: Move the ship.
  • Space: Shoot.
  • S: Enable or disable smooth scrolling.
  • D: Show or hide object bounds.
  • Q or ESC: Exit the tutorial.

The object-bound toggle uses Scene::setDebugObjectOverlay() with ::SDOT_STANDARD, leaving unrelated Scene and layer overlays unchanged.

Prerequisites

  • CRM64Pro GDK installed and configured with a supported C++17 compiler.
  • Tutorial package downloaded and fully extracted, preserving its folder structure.
  • Understand TMX object layers and SceneObject callbacks.
  • Included assets available under bin/Base/: tiled/ship/shooter.tmx, tiled/ship/ship.cdc.
  • Keep the Tutorial_bg resource from Tutorial 02 in Tutorial.cdc.

What you will learn

  • Create typed gameplay objects through a Scene factory.
  • Own a child sprite from each Scene object.
  • Spawn and remove runtime projectiles safely.
  • Combine spatial and pixel-perfect collision checks.
  • Configure repeating autoscroll layers.
  • Render a HUD from the Screen callback.

Walkthrough

1. Keep shared IDs in one state

Callbacks keep the Scene, HUD resources and stable object IDs together.

struct TutorialState
{
Scene* pScene = nullptr;
Sint32 idBgImage = -1;
Sint32 idHUDFont = -1;
bool bDebug = false;
Sint32 idPlayer = -1;
Sint32 idEnemy1 = -1;
Sint32 idEnemy2 = -1;
};

2. Own the entity sprite

Every gameplay object owns one child sprite and releases it with the object.

static Sint32 spawnSpriteChild(Sint32 iBaseSprite)
{
if(iBaseSprite < 0) return -1;
return Main::instance().spriteMgr().child(iBaseSprite);
}
virtual ~GameEntity()
{
if(iIDSprite >= 0)
{
Main::instance().spriteMgr().close(iIDSprite);
iIDSprite = -1;
}
}

3. Replace destroyed ships with explosions

The old child is closed first. If the explosion cannot be created, the object is removed cleanly.

void GameEntity::die(Scene* pScene, SceneLayerContext& rContext)
{
if(bIsDead) return;
bIsDead = true;
// Switch to a per-entity explosion child sprite
if(iIDSprite >= 0)
{
Main::instance().spriteMgr().close(iIDSprite);
iIDSprite = -1;
}
iIDSprite = spawnSpriteChild(Explosion::iIDSpriteBase);
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(!pS)
{
SceneLayerObject* pLayer = pScene ? pScene->accessLayerObject(rContext.idLayer) : nullptr;
if(pLayer) pLayer->closeObject(getID());
return;
}
pS->selectAnimIndex(0);
pS->resume();
}

4. Initialize runtime projectiles once

A projectile is initialized before placement. The guard prevents Scene insertion from creating a second child sprite.

void initialize() override
{
if(iIDSprite >= 0) return;
GameEntity::initialize();
iIDSprite = spawnSpriteChild(bIsEnemy ? iIDLaserEnemyBase : iIDLaserBase);
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS)
{
pS->selectAnimIndex(0);
Sint32 iW, iH;
pS->getOffset(&iW, &iH);
setSize((float)iW, (float)iH);
}
else setSize(16.0f, 16.0f);
}

5. Spawn player shots safely

The layer takes ownership only after addObject() succeeds; every earlier failure deletes the projectile locally.

Laser* pL = new(std::nothrow) Laser();
if(pL)
{
pL->initialize();
if(pL->iIDSprite < 0)
{
delete pL;
return;
}
float fLaserX = getX() + (getWidth() / 2.0f) - (pL->getWidth() / 2.0f);
pL->setPosition(fLaserX, getY() - pL->getHeight());
SceneLayerObject* pLayer = g_pState->pScene->accessLayerObject(2);
if(!pLayer || pLayer->addObject(pL) < 0)
{
delete pL;
return;
}
iShotCooldown = iShotCooldownTicks;
}

6. Use broad- and narrow-phase collision

The object layer finds nearby candidates, AABBs reject misses, and sprite overlap confirms visible hits.

vector<SceneObject*> vNeighbors;
pLayer->queryRegion(getX(), getY(), getWidth(), getHeight(), vNeighbors);
for(auto* pObj : vNeighbors)
{
if(pObj == this) continue;
bool bHit = false;
if(!bIsEnemy && pObj->getType() == "enemy") bHit = true;
else if(bIsEnemy && pObj->getType() == "player") bHit = true;
SDL_FRect rOverlap;
if(bHit && overlapsObject(pObj, &rOverlap))
{
GameEntity* pTarget = dynamic_cast<GameEntity*>(pObj);
if(pS1 && pS2)
{
if(!Main::instance().physics().overlapsSprite(pS1, getX(), getY(), pS2, pTarget->getX(), pTarget->getY(), &rOverlap))
{
continue; // No pixel hit, keep checking neighbors
}
}

7. Create typed objects from TMX

The TMX player and enemy types select their C++ classes.

class TutorialObjectFactory : public ISceneObjectFactory
{
public:
SceneObject* create(const string& rType) override
{
if(rType == "player") return new(std::nothrow) Player();
if(rType == "enemy") return new(std::nothrow) Enemy();
return nullptr;
}
};

8. Configure vertical autoscroll

Both map layers repeat vertically and share the same autoscroll speed.

for(Sint32 i = 1; i <= 2; i++)
{
SceneCameraParams mCam;
if(!g_pState->pScene->setLayerFeatures(i, SLF_REPEATY, true) ||
!g_pState->pScene->getCameraParams(i, &mCam))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to configure Scene layer %d.\n", i);
closeTutorial();
return -1;
}
mCam.ptAutoScrollSpeed = { fAutoScrollX, fAutoScrollY };
if(!g_pState->pScene->setCameraParams(i, mCam) ||
!g_pState->pScene->setCameraMode(i, SCM_AUTOSCROLL))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to configure Scene layer %d.\n", i);
closeTutorial();
return -1;
}
}

9. Validate the map contract

The tutorial requires one factory-created player and exactly two factory-created enemies.

pPlayer = dynamic_cast<Player*>(pObjLayer->findFirstByType("player"));
vector<SceneObject*> vEnemies;
pObjLayer->findAllByType("enemy", vEnemies);
Enemy* pEnemy1 = vEnemies.size() > 0 ? dynamic_cast<Enemy*>(vEnemies[0]) : nullptr;
Enemy* pEnemy2 = vEnemies.size() > 1 ? dynamic_cast<Enemy*>(vEnemies[1]) : nullptr;
if(!pPlayer || vEnemies.size() != 2 || !pEnemy1 || !pEnemy2)
{
mC64.logMgr().get()->msg(LL_CRITICAL, "Map must contain one player and exactly two enemies created by the object factory.\n");
closeTutorial();
return -1;
}
g_pState->idPlayer = pPlayer->getID();
g_pState->idEnemy1 = pEnemy1->getID();
g_pState->idEnemy2 = pEnemy2->getID();

10. Update, render and release resources

The fixed-rate Scene update drives gameplay. Cleanup follows ownership order and also covers partial setup.

// 3. Engine logic update (fixed rate by iLogicRate)
g_pState->pScene->update();
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_Shooter.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 Shooter
------------------------------------------------------------------------
Overview:
This tutorial loads a vertical shooter map, creates enemies and a player
from TMX objects, fires projectiles, and scrolls the background.
Key concepts:
- Load a TMX map with a Scene object factory.
- Create gameplay objects from TMX object types.
- Use sprites from SceneObject subclasses.
- Spawn and remove projectiles.
- Query object collisions from an object layer.
- Scroll Scene layers with camera autoscroll.
- Draw the HUD from the Screen render callback.
Layer layout:
- Layer 1-2: Autoscrolling world/background layers.
- Layer 2 object layer: player, enemies, projectiles, effects.
Controls:
- Cursor Keys: Move ship.
- Space: Shoot.
- S: Toggle smooth scrolling.
- D: Toggle debug AABB visuals.
- Q / ESC: Quit.
Loop order:
- Poll events and capture toggles.
- Poll real-time movement/shoot input.
- Run one fixed logic tick via Scene::update().
- Evaluate win/lose state after logic update.
- Render from callback using Scene::render().
Graphic assets:
- https://ansimuz.itch.io/spaceship-shooter-environment
------------------------------------------------------------------------ */
#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 = 480;
static const Sint32 iViewH = 480;
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 = 150.0f;
static const float fEnemySpeed = 80.0f;
static const float fEntrySpeed = 50.0f;
static const float fAutoScrollX = 0.0f;
static const float fAutoScrollY = -100.0f;
static const Sint32 iShotCooldownTicks = ((250 * iLogicRate) + 999) / 1000; // 250ms => ticks
static const float fPlayerHitShakeAmplitudePx = 10.0f;
static const Uint32 iPlayerHitShakeDurationMs = 500;
static const float fPlayerHitShakeFalloff = 4.5f;
// State shared by callbacks and Scene objects.
struct TutorialState
{
Scene* pScene = nullptr;
Sint32 idBgImage = -1;
Sint32 idHUDFont = -1;
bool bDebug = false;
Sint32 idPlayer = -1;
Sint32 idEnemy1 = -1;
Sint32 idEnemy2 = -1;
};
static TutorialState* g_pState = nullptr;
// -----------------------------------------------------------------------------------
// Custom game objects.
// -----------------------------------------------------------------------------------
// Base class for all shooter entities.
// Scene object layers manage these objects and include them in spatial queries.
class GameEntity : public SceneObject
{
public:
Sint32 iIDSprite = -1;
Sint32 iLife = 1;
bool bIsDead = false;
static Sint32 spawnSpriteChild(Sint32 iBaseSprite)
{
if(iBaseSprite < 0) return -1;
return Main::instance().spriteMgr().child(iBaseSprite);
}
virtual ~GameEntity()
{
if(iIDSprite >= 0)
{
Main::instance().spriteMgr().close(iIDSprite);
iIDSprite = -1;
}
}
// Scene calls initialize() after creating the object.
// Load gameplay values from TMX properties here.
void initialize() override
{
SceneObject::initialize();
iLife = getPropertyInt("life", iLife);
bIsDead = false;
}
virtual void die(Scene* pScene, SceneLayerContext& rContext);
// Scene calls update() during fixed logic steps.
void update(Scene* pScene, SceneLayerContext& rContext) override
{
if(bIsDead)
{
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
// If the death animation (explosion) is finished, remove the object
if(pS && pS->status() == GS_END)
{
SceneLayerObject* pLayer = pScene->accessLayerObject(rContext.idLayer);
if(pLayer) pLayer->closeObject(getID());
}
return;
}
// Fixed autoscroll compensation for this tutorial.
setPosition(getX() + fAutoScrollX * rContext.fDeltaTime, getY() + fAutoScrollY * rContext.fDeltaTime);
}
// render: draws the object.
// Interpolated world/screen data is provided by Scene in rContext.data.Object.
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 = rContext.data.Object.bRenderStateValid ? rContext.data.Object.fScreenX : pScreen.x;
float fScreenY = rContext.data.Object.bRenderStateValid ? rContext.data.Object.fScreenY : pScreen.y;
pSpr->setPosition(fScreenX, fScreenY);
pSpr->render();
}
};
// Explosion: a one-shot visual effect.
class Explosion : public GameEntity
{
public:
static Sint32 iIDSpriteBase;
void initialize() override
{
GameEntity::initialize();
iIDSprite = spawnSpriteChild(iIDSpriteBase);
if(iIDSprite >= 0)
{
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS) pS->selectAnimIndex(0);
}
}
void update(Scene* pScene, SceneLayerContext& rContext) override
{
GameEntity::update(pScene, rContext);
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS && pS->status() == GS_END)
{
SceneLayerObject* pLayer = pScene->accessLayerObject(rContext.idLayer);
if(pLayer) pLayer->closeObject(getID());
}
}
};
Sint32 Explosion::iIDSpriteBase = -1;
// Remove the entity after it is destroyed.
void GameEntity::die(Scene* pScene, SceneLayerContext& rContext)
{
if(bIsDead) return;
bIsDead = true;
// Switch to a per-entity explosion child sprite
if(iIDSprite >= 0)
{
Main::instance().spriteMgr().close(iIDSprite);
iIDSprite = -1;
}
iIDSprite = spawnSpriteChild(Explosion::iIDSpriteBase);
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(!pS)
{
SceneLayerObject* pLayer = pScene ? pScene->accessLayerObject(rContext.idLayer) : nullptr;
if(pLayer) pLayer->closeObject(getID());
return;
}
pS->resume();
}
// Laser: projectile with collision detection.
class Laser : public GameEntity
{
public:
static Sint32 iIDLaserBase;
static Sint32 iIDLaserEnemyBase;
bool bIsEnemy = false;
float fSpeedX = 0.0f;
float fSpeedY = -500.0f;
void initialize() override
{
if(iIDSprite >= 0) return;
GameEntity::initialize();
iIDSprite = spawnSpriteChild(bIsEnemy ? iIDLaserEnemyBase : iIDLaserBase);
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS)
{
Sint32 iW, iH;
pS->getOffset(&iW, &iH);
setSize((float)iW, (float)iH);
}
else setSize(16.0f, 16.0f);
}
void update(Scene* pScene, SceneLayerContext& rContext) override
{
GameEntity::update(pScene, rContext);
SceneLayerObject* pLayer = pScene->accessLayerObject(rContext.idLayer);
if(!pLayer) return;
// Apply internal movement logic
setPosition(getX() + fSpeedX * rContext.fDeltaTime, getY() + fSpeedY * rContext.fDeltaTime);
// Cleanup: remove projectile if it leaves the viewport
if(fSpeedY < 0) // Moving UP
{
if(getY() + getHeight() < rContext.fScrollY)
{
pLayer->closeObject(getID()); return;
}
}
else { // Moving DOWN
if(getY() > rContext.fScrollY + rContext.rViewport.h)
{
pLayer->closeObject(getID()); return;
}
}
// HEGA spatial query:
// Checking collision for a region (in this case, the object layer size).
vector<SceneObject*> vNeighbors;
pLayer->queryRegion(getX(), getY(), getWidth(), getHeight(), vNeighbors);
for(auto* pObj : vNeighbors)
{
if(pObj == this) continue;
bool bHit = false;
if(!bIsEnemy && pObj->getType() == "enemy") bHit = true;
else if(bIsEnemy && pObj->getType() == "player") bHit = true;
SDL_FRect rOverlap;
if(bHit && overlapsObject(pObj, &rOverlap))
{
GameEntity* pTarget = dynamic_cast<GameEntity*>(pObj);
if(pTarget && !pTarget->bIsDead)
{
// Narrow phase: Pixel-perfect collision check (Optimized with precomputed overlap)
Sprite* pS1 = Main::instance().spriteMgr().get(iIDSprite);
Sprite* pS2 = Main::instance().spriteMgr().get(pTarget->iIDSprite);
if(pS1 && pS2)
{
if(!Main::instance().physics().overlapsSprite(pS1, getX(), getY(), pS2, pTarget->getX(), pTarget->getY(), &rOverlap))
{
continue; // No pixel hit, keep checking neighbors
}
}
pTarget->iLife -= 20;
// Small camera shake feedback when player gets hit.
if(bIsEnemy && pTarget->getType() == "player")
{
pScene->setCameraShake(1, fPlayerHitShakeAmplitudePx, iPlayerHitShakeDurationMs, fPlayerHitShakeFalloff);
}
if(pTarget->iLife <= 0) pTarget->die(pScene, rContext);
}
pLayer->closeObject(getID());
return;
}
}
}
};
Sint32 Laser::iIDLaserBase = -1;
Sint32 Laser::iIDLaserEnemyBase = -1;
// Player: controlled ship.
class Player : public GameEntity
{
Sint32 iShotCooldown = 0;
public:
static Sint32 iIDSpriteBase;
float fMoveX = 0.0f;
float fMoveY = 0.0f;
bool bIsEntering = true;
float fEntryDistance = 0.0f;
void initialize() override
{
GameEntity::initialize();
iIDSprite = spawnSpriteChild(iIDSpriteBase);
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS)
{
Sint32 iW, iH;
pS->getOffset(&iW, &iH);
setSize((float)iW, (float)iH);
}
else setSize(32.0f, 32.0f);
bIsEntering = true;
fEntryDistance = 0.0f;
if(g_pState) g_pState->idPlayer = getID();
}
void update(Scene* pScene, SceneLayerContext& rContext) override
{
GameEntity::update(pScene, rContext);
if(bIsDead) return;
if(iShotCooldown > 0) iShotCooldown--;
float fDx = 0, fDy = 0;
if(bIsEntering)
{
// Scripted entry: move into screen
float fStep = fEntrySpeed * rContext.fDeltaTime;
fDy = -fStep;
fEntryDistance += fStep;
if(fEntryDistance >= 40.0f) bIsEntering = false;
}
else
{
// User movement
float fStep = fPlayerSpeed * rContext.fDeltaTime;
fDx = fMoveX * fStep;
fDy = fMoveY * fStep;
}
// Visual banking based on lateral velocity
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS)
{
if(fMoveX < 0) {
if(pS->getAnimCurrentIndex() != 1) pS->selectAnimIndex(1);
} // Left
else if(fMoveX > 0) {
if(pS->getAnimCurrentIndex() != 2) pS->selectAnimIndex(2);
} // Right
else if(pS->getAnimCurrentIndex() != 0) pS->selectAnimIndex(0); // Normal
}
if(fDx != 0 || fDy != 0)
{
float fNewX = getX() + fDx;
float fNewY = getY() + fDy;
// Keep the player inside the moving viewport.
if(fNewX < rContext.fScrollX) fNewX = rContext.fScrollX;
if(fNewX + getWidth() > rContext.fScrollX + iViewW) fNewX = rContext.fScrollX + iViewW - getWidth();
if(fNewY < rContext.fScrollY) fNewY = rContext.fScrollY;
if(fNewY + getHeight() > rContext.fScrollY + iViewH) fNewY = rContext.fScrollY + iViewH - getHeight();
setPosition(fNewX, fNewY);
}
}
void shoot()
{
if(!g_pState || !g_pState->pScene || bIsEntering) return;
if(iShotCooldown <= 0)
{
Laser* pL = new(std::nothrow) Laser();
if(pL)
{
pL->initialize();
if(pL->iIDSprite < 0)
{
delete pL;
return;
}
float fLaserX = getX() + (getWidth() / 2.0f) - (pL->getWidth() / 2.0f);
pL->setPosition(fLaserX, getY() - pL->getHeight());
SceneLayerObject* pLayer = g_pState->pScene->accessLayerObject(2);
if(!pLayer || pLayer->addObject(pL) < 0)
{
delete pL;
return;
}
iShotCooldown = iShotCooldownTicks;
}
}
}
};
Sint32 Player::iIDSpriteBase = -1;
// Enemy: ship with basic AI movement.
class Enemy : public GameEntity
{
public:
enum EnemyAI { AI_HOVER, AI_SINE };
private:
EnemyAI m_eAIState = AI_HOVER;
Sint32 m_iShotCooldown = 0;
Sint32 m_iNextFlank = 0;
float m_fOscillation = 0.0f;
float m_fXOffset = 0.0f;
float m_fOscSpeed = 2.0f;
float m_fOscAmplitude = 30.0f;
float m_fSpeedScale = 1.0f;
public:
static Sint32 iIDSpriteBase;
void initialize() override
{
GameEntity::initialize();
iIDSprite = spawnSpriteChild(iIDSpriteBase);
Sprite* pS = Main::instance().spriteMgr().get(iIDSprite);
if(pS)
{
Sint32 iW, iH;
pS->getOffset(&iW, &iH);
setSize((float)iW, (float)iH);
}
else setSize(32.0f, 32.0f);
Tool& mTool = Main::instance().tool();
m_iShotCooldown = static_cast<Sint32>(mTool.randMWC() % (iLogicRate * 2)); // up to ~2s
m_iNextFlank = iLogicRate * 3 + static_cast<Sint32>(mTool.randMWC() % (iLogicRate * 3)); // 3..6s
m_fXOffset = (float)(40 + (mTool.randMWC() % 80));
if(mTool.randMWC() % 2 == 0) m_fXOffset *= -1.0f;
m_fOscSpeed = 1.5f + (float)(mTool.randMWC() % 100) / 50.0f;
m_fOscAmplitude = 10.0f + (float)(mTool.randMWC() % 40);
m_fSpeedScale = 0.8f + (float)(mTool.randMWC() % 40) / 100.0f;
m_fOscillation = (float)(mTool.randMWC() % 360);
static Sint32 iAICounter = 0;
m_eAIState = (iAICounter++ % 2 == 0) ? AI_HOVER : AI_SINE;
}
void update(Scene* pScene, SceneLayerContext& rContext) override
{
GameEntity::update(pScene, rContext);
if(bIsDead) return;
const float fDt = rContext.fDeltaTime;
Tool& mTool = Main::instance().tool();
if(m_iShotCooldown > 0) m_iShotCooldown--;
if(m_iNextFlank > 0) m_iNextFlank--;
float fNextX = getX();
float fNextY = getY();
SceneLayerObject* pLayer = pScene->accessLayerObject(rContext.idLayer);
Player* pTargetPlayer = nullptr;
if(pLayer) pTargetPlayer = dynamic_cast<Player*>(pLayer->getObjectByID(g_pState->idPlayer));
if(m_eAIState == AI_HOVER)
{
if(m_iNextFlank <= 0)
{
m_fXOffset *= -1.0f;
m_iNextFlank = iLogicRate * 3 + static_cast<Sint32>(mTool.randMWC() % (iLogicRate * 4)); // 3..7s
}
if(pTargetPlayer)
{
float fTargetX = pTargetPlayer->getX() + (pTargetPlayer->getWidth() - getWidth()) / 2.0f + m_fXOffset;
float fDir = (fTargetX > getX()) ? 1.0f : -1.0f;
if(abs(fTargetX - getX()) > 5.0f) fNextX += fDir * fEnemySpeed * m_fSpeedScale * 1.5f * fDt;
}
m_fOscillation += m_fOscSpeed * fDt;
fNextY += ((rContext.fScrollY + 50.0f + (sinf(m_fOscillation) * m_fOscAmplitude)) - getY()) * 2.0f * fDt;
}
else // AI_SINE
{
fNextX += (fEnemySpeed * 1.2f * m_fSpeedScale) * fDt * (m_fXOffset > 0 ? 1.0f : -1.0f);
if(fNextX < rContext.fScrollX)
{
fNextX = rContext.fScrollX; m_fXOffset = 1.0f;
}
if(fNextX > rContext.fScrollX + rContext.rViewport.w - getWidth())
{
fNextX = rContext.fScrollX + rContext.rViewport.w - getWidth(); m_fXOffset = -1.0f;
}
m_fOscillation += m_fOscSpeed * 2.0f * fDt;
fNextY = rContext.fScrollY + 80.0f + (sinf(m_fOscillation) * 60.0f);
}
if(fNextX < rContext.fScrollX) fNextX = rContext.fScrollX;
if(fNextX > rContext.fScrollX + rContext.rViewport.w - getWidth()) fNextX = rContext.fScrollX + rContext.rViewport.w - getWidth();
setPosition(fNextX, fNextY);
// Enemy shooting.
if(m_iShotCooldown <= 0)
{
Laser* pL = new(std::nothrow) Laser();
if(pL)
{
pL->bIsEnemy = true;
pL->initialize();
if(pL->iIDSprite < 0)
{
delete pL;
}
else
{
float fStartX = getX() + (getWidth() - pL->getWidth()) / 2.0f;
float fStartY = getY() + getHeight();
pL->setPosition(fStartX, fStartY);
if(pTargetPlayer)
{
float fTx = pTargetPlayer->getX() + pTargetPlayer->getWidth() / 2.0f;
float fTy = pTargetPlayer->getY() + pTargetPlayer->getHeight() / 2.0f;
float fDx = fTx - fStartX;
float fDy = fTy - fStartY;
float fDist = sqrtf(fDx * fDx + fDy * fDy);
if(fDist > 0)
{
float fShotSpeed = 300.0f;
pL->fSpeedX = (fDx / fDist) * fShotSpeed;
pL->fSpeedY = (fDy / fDist) * fShotSpeed;
}
else pL->fSpeedY = 300.0f;
}
else pL->fSpeedY = 300.0f;
if(!pLayer || pLayer->addObject(pL) < 0) delete pL;
}
}
m_iShotCooldown = (iLogicRate * 8) / 10 + static_cast<Sint32>(mTool.randMWC() % ((iLogicRate * 7) / 10 + 1)); // ~0.8..1.5s
}
}
};
Sint32 Enemy::iIDSpriteBase = -1;
// -----------------------------------------------------------------------------------
// Infrastructure (factory & main loop)
// -----------------------------------------------------------------------------------
// TutorialObjectFactory: links Tiled object types to C++ classes.
class TutorialObjectFactory : public ISceneObjectFactory
{
public:
SceneObject* create(const string& rType) override
{
if(rType == "player") return new(std::nothrow) Player();
if(rType == "enemy") return new(std::nothrow) Enemy();
return nullptr;
}
};
// renderWrapper: global rendering callback.
Sint32 renderWrapper(Sint32)
{
if(!g_pState || !g_pState->pScene) return 0;
Main& mC64 = Main::instance();
// 1. Background image
if(g_pState->idBgImage > 0)
{
Image* pBG = mC64.imageMgr().get(g_pState->idBgImage);
if(pBG) pBG->render();
}
// 2. Scene (handles all layers and entities)
g_pState->pScene->render();
// 3. HUD rendering
Font* pFont = mC64.fontMgr().get(g_pState->idHUDFont);
if(pFont)
{
char szBuf[64];
Sint32 iTextW = 0;
SceneLayerObject* pLayer = g_pState->pScene->accessLayerObject(2);
Player* pP = nullptr;
if(pLayer) pP = dynamic_cast<Player*>(pLayer->getObjectByID(g_pState->idPlayer));
if(pP)
{
iTextW = pFont->getWidth("Player");
pFont->setPosition(static_cast<float>(40 - iTextW / 2), 20);
pFont->render("Player");
snprintf(szBuf, 64, "%d", pP->iLife);
iTextW = pFont->getWidth(szBuf);
pFont->setPosition(static_cast<float>(40 - iTextW / 2), 40);
pFont->render(szBuf);
}
if(pLayer)
{
Sint32 aEnemyIDs[2] = { g_pState->idEnemy1, g_pState->idEnemy2 };
for(Sint32 i = 0; i < 2; i++)
{
if(aEnemyIDs[i] == -1) continue;
Enemy* pE = dynamic_cast<Enemy*>(pLayer->getObjectByID(aEnemyIDs[i]));
if(pE)
{
snprintf(szBuf, 64, "Enemy %d", i + 1);
iTextW = pFont->getWidth(szBuf);
float fBaseX = static_cast<float>(iScreenW - 40 - iTextW / 2);
float fBaseY = static_cast<float>(20 + (i * 60));
pFont->setPosition(fBaseX, fBaseY);
pFont->render(szBuf);
snprintf(szBuf, 64, "%d", pE->iLife);
iTextW = pFont->getWidth(szBuf);
pFont->setPosition(static_cast<float>(iScreenW - 40 - iTextW / 2), fBaseY + 20);
pFont->render(szBuf);
}
}
}
}
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;
// Memory management
atexit(CMem::destroy);
Main& mC64 = Main::instance();
TutorialState state;
g_pState = &state;
mC64.logMgr().get()->init("Tutorial_16_Scene_Shooter", LL_DEBUG, LM_STDOUT | LM_FILE, OUTPUTDIR"Tutorial_16_Scene_Shooter.log");
// Core initialization
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->setDriver(eRenderer);
pScreen->setSize(iScreenW, iScreenH);
pScreen->setTitle("Tutorial 16 Scene - Shooter");
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 game resources
g_pState->idBgImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_BG_CDC_NAME);
g_pState->idHUDFont = mC64.fontMgr().getBuiltin("CourierNew10White");
if(g_pState->idBgImage <= 0 || g_pState->idHUDFont < 0)
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to load the tutorial interface resources.\n");
closeTutorial();
return -1;
}
Sint32 idCDC = mC64.archiveMgr().load(BASEDIR "tiled/ship/ship.cdc");
if(idCDC < 0)
{
mC64.logMgr().get()->msg(LL_CRITICAL, "ERROR: resource archive missing: %s\n", BASEDIR "tiled/ship/ship.cdc");
closeTutorial();
return -1;
}
Player::iIDSpriteBase = mC64.spriteMgr().load(idCDC, "ship");
Enemy::iIDSpriteBase = mC64.spriteMgr().load(idCDC, "enemy");
Explosion::iIDSpriteBase = mC64.spriteMgr().load(idCDC, "explosion");
Laser::iIDLaserBase = mC64.spriteMgr().load(idCDC, "laser");
Laser::iIDLaserEnemyBase = mC64.spriteMgr().load(idCDC, "laser_enemy");
mC64.archiveMgr().close(idCDC);
if(Player::iIDSpriteBase < 0 || Enemy::iIDSpriteBase < 0 || Explosion::iIDSpriteBase < 0 ||
Laser::iIDLaserBase < 0 || Laser::iIDLaserEnemyBase < 0)
{
mC64.logMgr().get()->msg(LL_CRITICAL, "Failed to load one or more shooter sprites.\n");
closeTutorial();
return -1;
}
// Map loading
static TutorialObjectFactory mFactory;
Sint32 idScene = mC64.sceneMgr().loadFromFile(BASEDIR "tiled/ship/shooter.tmx", &mFactory);
if(idScene < 0)
{
mC64.logMgr().get()->msg(LL_CRITICAL, "ERROR: map file missing: %s\n", BASEDIR "tiled/ship/shooter.tmx");
closeTutorial();
return -1;
}
g_pState->pScene = mC64.sceneMgr().get(idScene);
if(!g_pState->pScene)
{
closeTutorial();
return -1;
}
// Viewport & scrolling configuration
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;
}
for(Sint32 i = 1; i <= 2; i++)
{
if(!g_pState->pScene->setLayerFeatures(i, SLF_REPEATY, true) ||
!g_pState->pScene->getCameraParams(i, &mCam))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to configure Scene layer %d.\n", i);
closeTutorial();
return -1;
}
mCam.ptAutoScrollSpeed = { fAutoScrollX, fAutoScrollY };
if(!g_pState->pScene->setCameraParams(i, mCam) ||
!g_pState->pScene->setCameraMode(i, SCM_AUTOSCROLL))
{
mC64.logMgr().get()->msg(LL_ERROR, "Failed to configure Scene layer %d.\n", i);
closeTutorial();
return -1;
}
}
// Initialization: find entities created from TMX
Player* pPlayer = nullptr;
SceneLayerObject* pObjLayer = g_pState->pScene->accessLayerObject(2);
if(!pObjLayer)
{
mC64.logMgr().get()->msg(LL_CRITICAL, "Layer 2 must be an object layer.\n");
closeTutorial();
return -1;
}
pPlayer = dynamic_cast<Player*>(pObjLayer->findFirstByType("player"));
vector<SceneObject*> vEnemies;
pObjLayer->findAllByType("enemy", vEnemies);
Enemy* pEnemy1 = vEnemies.size() > 0 ? dynamic_cast<Enemy*>(vEnemies[0]) : nullptr;
Enemy* pEnemy2 = vEnemies.size() > 1 ? dynamic_cast<Enemy*>(vEnemies[1]) : nullptr;
if(!pPlayer || vEnemies.size() != 2 || !pEnemy1 || !pEnemy2)
{
mC64.logMgr().get()->msg(LL_CRITICAL, "Map must contain one player and exactly two enemies created by the object factory.\n");
closeTutorial();
return -1;
}
g_pState->idPlayer = pPlayer->getID();
g_pState->idEnemy1 = pEnemy1->getID();
g_pState->idEnemy2 = pEnemy2->getID();
g_pState->pScene->setDebugObjectOverlay(SDO_OBJECT_AABB, SDOT_STANDARD, false);
g_pState->pScene->setDebugOverlayColor(SDO_OBJECT_AABB, 0x00FF00FF);
mC64.logMgr().get()->msg(LL_INFO, "\nCONTROLS:\n [Cursors] Move\n [Space] Shoot\n [S] Smooth scroll\n [D] Debug boxes\n [Q/Esc] Quit\n\n");
// Main game loop
SDL_Event ev;
bool bDone = false;
bool bSmooth = false;
while(!bDone)
{
// 1. Event handling (Toggle modes and window events)
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)
{
bSmooth = !bSmooth;
for(Sint32 i = 1; i <= g_pState->pScene->getLayerCount(); i++) g_pState->pScene->setLayerFeatures(i, SLF_SMOOTHSCROLL, bSmooth);
}
if(ev.key.key == SDLK_D)
{
g_pState->bDebug = !g_pState->bDebug;
g_pState->pScene->setDebugObjectOverlay(SDO_OBJECT_AABB, SDOT_STANDARD, g_pState->bDebug);
}
}
}
if(bDone) break;
SceneLayerObject* pLayer = g_pState->pScene->accessLayerObject(2);
pPlayer = pLayer ? dynamic_cast<Player*>(pLayer->getObjectByID(g_pState->idPlayer)) : nullptr;
// 2. Real-time input polling (Movement and action)
if(pPlayer && !pPlayer->bIsDead && !pPlayer->bIsEntering)
{
float fDX = 0.0f;
float fDY = 0.0f;
if(mC64.getKeyState(SDLK_UP)) fDY -= 1.0f;
if(mC64.getKeyState(SDLK_DOWN)) fDY += 1.0f;
if(mC64.getKeyState(SDLK_LEFT)) fDX -= 1.0f;
if(mC64.getKeyState(SDLK_RIGHT)) fDX += 1.0f;
if(fDX != 0.0f && fDY != 0.0f)
{
fDX *= 0.7071f;
fDY *= 0.7071f;
}
pPlayer->fMoveX = fDX;
pPlayer->fMoveY = fDY;
if(mC64.getKeyState(SDLK_SPACE)) pPlayer->shoot();
}
// 3. Engine logic update (fixed rate by iLogicRate)
g_pState->pScene->update();
// 4. Victory / defeat checks after logic update
pLayer = g_pState->pScene->accessLayerObject(2);
pPlayer = pLayer ? dynamic_cast<Player*>(pLayer->getObjectByID(g_pState->idPlayer)) : nullptr;
if(pLayer)
{
bool bEnemyAlive = (pLayer->getObjectByID(g_pState->idEnemy1) != nullptr) || (pLayer->getObjectByID(g_pState->idEnemy2) != nullptr);
bool bPlayerAlive = (pPlayer != nullptr);
if(!bEnemyAlive && bPlayerAlive)
{
g_pState->pScene->pause();
mC64.tool().messageBox("Victory!", "All enemies destroyed!", MBB_OK, MBT_INFO);
g_pState->pScene->resume();
bDone = true;
}
else if(!bPlayerAlive)
{
g_pState->pScene->pause();
mC64.tool().messageBox("Game Over", "Your ship was destroyed.", MBB_OK, MBT_ERROR);
g_pState->pScene->resume();
bDone = true;
}
}
}
// Resource cleanup
mC64.sceneMgr().info();
closeTutorial();
return 0;
}
Sint32 close(Sint32 idCDC) override
Close and destroy a CDC object.
Definition CoreArchive.cpp:2476
Sint32 load(const string &sFile, eArchiveOpenMode eMode=AOM_READ_ONLY, const Uint8 *pKey=nullptr, Uint32 iKeySize=0)
Load a CDC file.
Definition CoreArchive.cpp:2630
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
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 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
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 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 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
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
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_DEBUG
Detailed information for flow control.
Definition CRM64Pro.h:765
@ 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
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
ArchiveMgr & archiveMgr()
Get reference to Archive Manager.
Definition CoreMain.cpp:736
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
SceneLayerObject * accessLayerObject(Sint32 iLayer)
Get an object layer by Scene layer index.
Definition Scene.cpp:1167
bool setCameraShake(Sint32 iLayer, float fAmplitudePX, Uint32 iDurationMS, float fFalloff=5.0f)
Set a transient render-only camera shake for a layer.
Definition Scene.cpp:1091
@ SCM_AUTOSCROLL
Automatic scrolling by velocity (SceneCameraParams::ptAutoScrollSpeed), then optional clamping.
Definition CRM64Pro.h:10182
@ SLF_REPEATY
Enable the layer image to be repeated along the Y-axis.
Definition CRM64Pro.h:10077
@ SLF_SMOOTHSCROLL
Enable the smooth rendering feature. Check render() for further details.
Definition CRM64Pro.h:10080
@ SDO_OBJECT_AABB
Draw object AABBs for selected object types.
Definition CRM64Pro.h:10093
@ SDOT_STANDARD
Standard and user-defined object types.
Definition CRM64Pro.h:10109
Sint32 addObject(SceneObject *pObj)
Add an object to this layer.
Definition SceneLayerObject.cpp:3165
SceneObject * getObjectByID(Sint32 idObject)
Get object by Tiled ID.
Definition SceneLayerObject.cpp:3301
SceneObject * findFirstByType(const string &sType)
Find first object matching an exact type string.
Definition SceneLayerObject.cpp:3349
void findAllByType(const string &sType, vector< SceneObject * > &vResults)
Find all objects matching an exact type string.
Definition SceneLayerObject.cpp:3393
bool closeObject(Sint32 id)
Destroy an object by its ID.
Definition SceneLayerObject.cpp:3203
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 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 render(Sint32 idRes=0)
Render the sprite.
Definition GraphicsSprite.cpp:1395
bool selectAnimIndex(Sint32 iIndex)
Select an animation by index.
Definition GraphicsSprite.cpp:829
Sint32 load(const string &sCDCFile, const string &sName)
Load a sprite stored in a CDC file.
Definition GraphicsSprite.cpp:1907
bool getOffset(Sint32 *iX, Sint32 *iY) const
Get the sprite offsets (frames and animations).
Definition GraphicsSprite.cpp:727
Sint32 getAnimCurrentIndex() const
Get current animation index.
Definition GraphicsSprite.cpp:788
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 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
Uint32 randMWC()
Generates a random 32-bit integer using Multiply-with-carry(MWC) algorithm.
Definition CoreTool.cpp:594
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_ERROR
Error icon is displayed.
Definition CRM64Pro.h:2500
@ MBT_INFO
Information icon is displayed.
Definition CRM64Pro.h:2502
CRM64Pro GDK.
Definition AudioTrack.cpp:36
Camera configuration parameters.
Definition CRM64Pro.h:10194
SDL_FPoint ptAutoScrollSpeed
Camera autoscroll speed in pixels/second for SCM_AUTOSCROLL. Positive X moves right,...
Definition CRM64Pro.h:10196
Layer Context structure.
Definition CRM64Pro.h:10223
float fScrollY
Current Logic/Render Y scroll position.
Definition CRM64Pro.h:10228
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
float fScrollX
Current Logic/Render X scroll position.
Definition CRM64Pro.h:10227

Navigation

Previous: Tutorial 16.3 - Scene Platform

Back to the tutorials index

Next: Tutorial 16.5 - Scene War Game