CRM64Pro GDK v0.80.0
A free cross-platform game development kit built on top of SDL 3.0
Loading...
Searching...
No Matches
Tutorial 10: Particle effects

Overview

This tutorial introduces ParticleMgr and ParticleEmitter. A continuous emitter follows the mouse, number keys switch built-in presets and left clicks create one-shot explosions.

A Physics rectangle demonstrates kill, stop and bounce responses while the particle system is updated at the fixed logic rate and rendered from the screen callback.

Final result

CRM64Pro Tutorial 10 particle effects

Runtime controls

  • Mouse: Move the active emitter.
  • Left click: Create an explosion one-shot.
  • 1-5: Select fire, smoke, snow, sparks or spell aura.
  • O: Cycle none, kill, stop and bounce collision modes.
  • D: Show or hide the debug window.
  • Grave key: Show or hide the built-in console.
  • 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.
  • Run Tutorials 02 and 04 first so Tutorial.cdc contains the background and cursor resources.
  • Write access to the platform output directory for the log.

What you will learn

  • Create a ParticleEmitter through ParticleMgr.
  • Apply built-in particle presets.
  • Configure maximum particles and emitter position.
  • Bind a Physics collision set to particle emitters.
  • Select particle collision responses.
  • Update and render ParticleMgr separately.
  • Create self-closing one-shot particle effects.

Step by step

1. Load the shared resources

Load the background and cursor created by earlier tutorials, then select the cursor using its existing default hotspot.

mLog.msg(LL_INFO, " Load cursor from CDC ... ");
state.idCursor = mC64.cursorMgr().load(OUTPUT_CDC, RESOURCE_CURSOR_CDC_NAME);
Cursor* pCursor = mC64.cursorMgr().get(state.idCursor);
if(state.idCursor < 0 || pCursor == nullptr)
{
logTaskFailed(mLog, state.idCursor);
Main::terminate();
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Select cursor ... ");
if(mC64.cursorMgr().select(state.idCursor) < 0 || !mC64.cursorMgr().show())
{
logTaskFailed(mLog);
Main::terminate();
return -1;
}
logTaskOk(mLog);

2. Define the available presets

A compact table maps keys 1-5 to the built-in preset and its HUD label.

static const TutorialPreset g_Presets[] =
{
{ PEP_FIRE, "Fire" },
{ PEP_SMOKE, "Smoke" },
{ PEP_SNOW, "Snow" },
{ PEP_SPARKS, "Sparks" },
{ PEP_SPELL_AURA, "Spell aura" }
};
@ PEP_SPELL_AURA
Additive magical swirl and spell aura.
Definition CRM64Pro.h:6556

3. Create the emitter and collider

ParticleMgr creates the emitter. Physics owns the rectangle collision set that is later shared with the continuous emitter and one-shots.

state.idEmitter = mC64.particleMgr().create("TutorialMouseEmitter");
state.idCollisionSet = mC64.physics().createCollisionSet("TutorialParticleCollider");
const Sint32 idCollider = mC64.physics().addColliderRectangle(state.idCollisionSet,
TUTORIAL_COLLIDER_X, TUTORIAL_COLLIDER_Y, TUTORIAL_COLLIDER_W, TUTORIAL_COLLIDER_H);
if(state.idEmitter <= 0 || state.idCollisionSet <= 0 || idCollider <= 0
|| getEmitter(state) == nullptr || !applyPreset(state, 0))
{
logTaskFailed(mLog, state.idEmitter);
Main::terminate();
return -1;
}
logTaskOk(mLog);

4. Apply a preset completely

The preset, capacity, position, collision settings and playback state are one configuration operation. The selected index changes only after every call succeeds.

// Apply the selected particle preset.
static bool applyPreset(TutorialState& rState, Sint32 iPreset)
{
if(iPreset < 0 || iPreset >= static_cast<Sint32>(sizeof(g_Presets) / sizeof(g_Presets[0]))) return false;
ParticleEmitter* pEmitter = getEmitter(rState);
if(!pEmitter) return false;
if(!pEmitter->applyPreset(g_Presets[iPreset].ePreset)
|| !pEmitter->setMaxParticles(1600)
|| !pEmitter->setPosition(rState.fMouseX, rState.fMouseY)
|| !pEmitter->setCollisionSet(rState.idCollisionSet)
|| !pEmitter->setCollisionMode(rState.eCollisionMode, 0.75f)
|| !pEmitter->play()) return false;
rState.iPreset = iPreset;
return true;
}

5. Change collision response safely

The next mode is applied to the emitter before the tutorial state is updated. This keeps the HUD and particle behavior synchronized if configuration fails.

// Cycle the tutorial particle collision response.
static bool cycleCollisionMode(TutorialState& rState)
{
eParticleCollisionMode eMode;
switch(rState.eCollisionMode)
{
case PCM_NONE: eMode = PCM_KILL; break;
case PCM_KILL: eMode = PCM_STOP; break;
case PCM_STOP: eMode = PCM_BOUNCE; break;
default: eMode = PCM_NONE; break;
}
ParticleEmitter* pEmitter = getEmitter(rState);
if(!pEmitter || !pEmitter->setCollisionMode(eMode, 0.75f)) return false;
rState.eCollisionMode = eMode;
return true;
}

6. Update particles at the logic rate

Move the continuous emitter, then advance every particle emitter with the engine's current logic delta time.

// Update the tutorial state for the current frame.
static void updateLogic(TutorialState& rState, float fDeltaTime)
{
ParticleEmitter* pEmitter = getEmitter(rState);
if(pEmitter) pEmitter->setPosition(rState.fMouseX, rState.fMouseY);
Main::instance().particleMgr().update(fDeltaTime);
rState.iLogicFrames++;
}

7. Render particles from the callback

The background is drawn first, followed by a glow, the particles, the collider outline and the HUD.

// Draw the tutorial frame for the current state.
static void renderFrame(Image* pBgImage, Font* pFont, TutorialState& rState)
{
if(pBgImage) pBgImage->render();
Main& mC64 = Main::instance();
mC64.gfx().drawGlow(rState.fMouseX, rState.fMouseY, 96.0f, 255, 150, 40, 120, 0.55f);
mC64.particleMgr().render();
renderCollider(mC64, rState.eCollisionMode);
renderHUD(pFont, rState);
}

8. Select presets and collider modes

Preset changes report failure without claiming a different active preset. The O key cycles collision modes only when the emitter accepts the change.

else if(event.key.key >= SDLK_1 && event.key.key <= SDLK_5)
{
const Sint32 iPreset = static_cast<Sint32>(event.key.key - SDLK_1);
if(applyPreset(state, iPreset))
mLog.msg(LL_INFO, " Preset: %s\n", g_Presets[state.iPreset].szName);
else
mLog.msg(LL_ERROR, " Preset change failed\n");
}
else if(event.key.key == SDLK_D)
{
toggleDebugWindow(pDebug);
}
else if(event.key.key == SDLK_O && cycleCollisionMode(state))
{
mLog.msg(LL_INFO, " Collider: %s\n", getCollisionModeName(state.eCollisionMode));
}

9. Create explosion one-shots

ParticleMgr::playOneShot() creates and starts a temporary emitter. When it is available, attach the same collision set and response used by the main emitter.

else if(event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT)
{
const Sint32 idOneShot = mC64.particleMgr().playOneShot(
PEP_EXPLOSION, event.button.x, event.button.y);
ParticleEmitter* pOneShot = mC64.particleMgr().get(idOneShot);
if(pOneShot)
{
pOneShot->setCollisionSet(state.idCollisionSet);
pOneShot->setCollisionMode(state.eCollisionMode, 0.75f);
}
}

10. Finish the loop cleanly

A quit request exits before another particle update. Otherwise the cursor position and current logic delta drive the next step.

if(!bRunning) break;
state.fMouseX = mC64.cursorMgr().getX();
state.fMouseY = mC64.cursorMgr().getY();
state.iCurrentRenderRate = static_cast<Sint32>(mC64.timer().getCurrentRFR());
state.iCurrentLogicRate = static_cast<Sint32>(mC64.timer().getCurrentLFR());
updateLogic(state, mC64.getLogicDeltaTime());

11. Close emitters before physics

Particle emitters refer to the collision set, so close them before destroying that set.

mLog.msg(LL_INFO, " Close particle emitters ... ");
mC64.particleMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close particle collider ... ");
if(mC64.physics().closeCollisionSet(state.idCollisionSet) < 0) logTaskFailed(mLog);
else logTaskOk(mLog);

Complete source

The tutorial source is authoritative. The following listing is included directly from validation/source/Tutorial_10_Particles.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 10: Particles
------------------------------------------------------------------------
Overview:
This tutorial introduces ParticleMgr and ParticleEmitter with a small
emitter that follows the mouse and one-shot effects on click.
Key concepts:
- Create a ParticleEmitter through ParticleMgr.
- Apply built-in particle presets.
- Bind a Physics collision set and select its particle response.
- Update particles at the fixed logic rate.
- Render particles from the screen render callback.
- Spawn one-shot particle effects.
Controls:
- Mouse: Move the active emitter.
- Left Click: Spawn an explosion one-shot.
- 1..5: Switch particle preset.
- O: Cycle particle collider mode.
- D: Toggle debug window.
- Grave: Toggle console.
- Q / ESC: Quit.
Loop order:
- Process input events.
- Run one fixed logic update.
- Update ParticleMgr with the logic delta time.
- Let Main::update() call the screen render callback.
------------------------------------------------------------------------ */
#include "CRM64Pro.h"
#include "RunParameter.h"
using namespace CRM64Pro;
#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 RESOURCE_CURSOR_CDC_NAME "Tutorial_cursor"
#define OUTPUT_CDC OUTPUTDIR"Tutorial.cdc"
#define TUTORIAL_COLLIDER_X (320.0f)
#define TUTORIAL_COLLIDER_Y (200.0f)
#define TUTORIAL_COLLIDER_W (320.0f)
#define TUTORIAL_COLLIDER_H (20.0f)
static const Sint32 iLogicRate = 60;
static const Sint32 iRenderRate = 0;
static const Sint32 iScreenW = 960;
static const Sint32 iScreenH = 540;
struct TutorialPreset
{
const char* szName;
};
static const TutorialPreset g_Presets[] =
{
{ PEP_FIRE, "Fire" },
{ PEP_SMOKE, "Smoke" },
{ PEP_SNOW, "Snow" },
{ PEP_SPARKS, "Sparks" },
{ PEP_SPELL_AURA, "Spell aura" }
};
struct TutorialState
{
Sint32 idBgImage = -1;
Sint32 idCursor = -1;
Sint32 idBuiltInFont = -1;
Sint32 idDebugWindow = -1;
Sint32 idEmitter = -1;
Sint32 idCollisionSet = -1;
float fMouseX = iScreenW * 0.5f;
float fMouseY = iScreenH * 0.5f;
Sint32 iPreset = 0;
Sint32 iCurrentRenderRate = 0;
Sint32 iCurrentLogicRate = 0;
Sint32 iLogicFrames = 0;
eParticleCollisionMode eCollisionMode = PCM_NONE;
};
// Record a successful tutorial task.
static void logTaskOk(Log& mLog)
{
mLog.msg(LL_DEBUG, "OK\n");
}
// Record a failed tutorial task.
static void logTaskFailed(Log& mLog, Sint32 iCode)
{
mLog.msg(LL_ERROR, "FAILED (Code %d)\n", iCode);
}
// Record a failed tutorial task.
static void logTaskFailed(Log& mLog)
{
mLog.msg(LL_ERROR, "FAILED\n");
}
// Get the particle emitter used by the tutorial.
static ParticleEmitter* getEmitter(TutorialState& rState)
{
return Main::instance().particleMgr().get(rState.idEmitter);
}
// Return the display name for one particle collision mode.
static const char* getCollisionModeName(eParticleCollisionMode eMode)
{
switch(eMode)
{
case PCM_NONE: return "NONE";
case PCM_KILL: return "KILL";
case PCM_STOP: return "STOP";
case PCM_BOUNCE: return "BOUNCE";
default: return "UNKNOWN";
}
}
// Cycle the tutorial particle collision response.
static bool cycleCollisionMode(TutorialState& rState)
{
switch(rState.eCollisionMode)
{
case PCM_NONE: eMode = PCM_KILL; break;
case PCM_KILL: eMode = PCM_STOP; break;
case PCM_STOP: eMode = PCM_BOUNCE; break;
default: eMode = PCM_NONE; break;
}
ParticleEmitter* pEmitter = getEmitter(rState);
if(!pEmitter || !pEmitter->setCollisionMode(eMode, 0.75f)) return false;
rState.eCollisionMode = eMode;
return true;
}
// Apply the selected particle preset.
static bool applyPreset(TutorialState& rState, Sint32 iPreset)
{
if(iPreset < 0 || iPreset >= static_cast<Sint32>(sizeof(g_Presets) / sizeof(g_Presets[0]))) return false;
ParticleEmitter* pEmitter = getEmitter(rState);
if(!pEmitter) return false;
if(!pEmitter->applyPreset(g_Presets[iPreset].ePreset)
|| !pEmitter->setMaxParticles(1600)
|| !pEmitter->setPosition(rState.fMouseX, rState.fMouseY)
|| !pEmitter->setCollisionSet(rState.idCollisionSet)
|| !pEmitter->setCollisionMode(rState.eCollisionMode, 0.75f)
|| !pEmitter->play()) return false;
rState.iPreset = iPreset;
return true;
}
// Update the tutorial state for the current frame.
static void updateLogic(TutorialState& rState, float fDeltaTime)
{
ParticleEmitter* pEmitter = getEmitter(rState);
if(pEmitter) pEmitter->setPosition(rState.fMouseX, rState.fMouseY);
Main::instance().particleMgr().update(fDeltaTime);
rState.iLogicFrames++;
}
// Draw text at the requested screen position.
static void renderText(Font* pFont, Sint32 iX, Sint32 iY, const char* szText)
{
if(!pFont) return;
pFont->setPosition(iX, iY);
pFont->render(szText);
}
// Draw the fixed particle collider used by the tutorial.
static void renderCollider(Main& mC64, eParticleCollisionMode eMode)
{
Uint8 iRed = 180;
Uint8 iGreen = 180;
Uint8 iBlue = 180;
if(eMode == PCM_KILL) { iRed = 255; iGreen = 64; iBlue = 64; }
else if(eMode == PCM_STOP) { iRed = 255; iGreen = 220; iBlue = 64; }
else if(eMode == PCM_BOUNCE) { iRed = 64; iGreen = 180; iBlue = 255; }
const float fX2 = TUTORIAL_COLLIDER_X + TUTORIAL_COLLIDER_W;
const float fY2 = TUTORIAL_COLLIDER_Y + TUTORIAL_COLLIDER_H;
mC64.gfx().line(TUTORIAL_COLLIDER_X, TUTORIAL_COLLIDER_Y, fX2, TUTORIAL_COLLIDER_Y,
iRed, iGreen, iBlue, 255);
mC64.gfx().line(fX2, TUTORIAL_COLLIDER_Y, fX2, fY2, iRed, iGreen, iBlue, 255);
mC64.gfx().line(fX2, fY2, TUTORIAL_COLLIDER_X, fY2, iRed, iGreen, iBlue, 255);
mC64.gfx().line(TUTORIAL_COLLIDER_X, fY2, TUTORIAL_COLLIDER_X, TUTORIAL_COLLIDER_Y,
iRed, iGreen, iBlue, 255);
}
// Draw the tutorial status information.
static void renderHUD(Font* pFont, TutorialState& rState)
{
if(!pFont) return;
ParticleEmitter* pEmitter = getEmitter(rState);
char szLine[160];
snprintf(szLine, sizeof(szLine), "Tutorial 10: Particles Render: %d fps Logic: %d fps",
rState.iCurrentRenderRate, rState.iCurrentLogicRate);
renderText(pFont, 10, 10, szLine);
snprintf(szLine, sizeof(szLine), "Preset: %s Live: %d Collider: %s",
g_Presets[rState.iPreset].szName, pEmitter ? pEmitter->getLiveCount() : 0,
getCollisionModeName(rState.eCollisionMode));
renderText(pFont, 10, 32, szLine);
renderText(pFont, 10, 510, "Mouse moves emitter Left click explosion 1..5 preset O collider D debug");
}
// Draw the tutorial frame for the current state.
static void renderFrame(Image* pBgImage, Font* pFont, TutorialState& rState)
{
if(pBgImage) pBgImage->render();
Main& mC64 = Main::instance();
mC64.gfx().drawGlow(rState.fMouseX, rState.fMouseY, 96.0f, 255, 150, 40, 120, 0.55f);
mC64.particleMgr().render();
renderCollider(mC64, rState.eCollisionMode);
renderHUD(pFont, rState);
}
// Adapt the renderer to the engine callback signature.
static Sint32 renderWrapper(Sint32, void* pObj)
{
TutorialState* pState = static_cast<TutorialState*>(pObj);
if(!pState) return 0;
Main& mC64 = Main::instance();
Image* pBgImage = mC64.imageMgr().get(pState->idBgImage);
Font* pFont = mC64.fontMgr().get(pState->idBuiltInFont);
renderFrame(pBgImage, pFont, *pState);
return 0;
}
// Show or hide the debug window.
static void toggleDebugWindow(DebugWindow* pDebug)
{
if(!pDebug) return;
if(pDebug->baseWidget().status() == GS_SHOWN) pDebug->baseWidget().hide();
else pDebug->baseWidget().show();
}
// 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();
Log& mLog = *mC64.logMgr().get();
TutorialState state;
mLog.init("Tutorial_10_Particles", LL_DEBUG, LM_FILE | LM_STDOUT, OUTPUTDIR"Tutorial_10_Particles.log");
mLog.msg(LL_INFO, "\nSETUP: screen and timing\n");
mLog.msg(LL_INFO, " Initialize timer ... ");
if(!mC64.timer().init())
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Set frame rates (render=%d, logic=%d) ... ", iRenderRate, iLogicRate);
if(!mC64.timer().setRate(iRenderRate, iLogicRate))
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
Screen* pScreen = mC64.configMgr().get();
pScreen->setSize(iScreenW, iScreenH);
pScreen->setDriver(eRenderer);
pScreen->setTitle("CRM64Pro - Tutorial 10: Particles");
mLog.msg(LL_INFO, " Create screen (%dx%d) ... ", iScreenW, iScreenH);
if(pScreen->show() < 0)
{
mLog.msg(LL_CRITICAL, "FAILED\n");
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Set render callback ... ");
if(!pScreen->setRenderCallback([&state](Sint32 iMode) { return renderWrapper(iMode, &state); }))
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, "\nRESOURCES: load from CDC\n");
mLog.msg(LL_INFO, " Load background image from CDC ... ");
state.idBgImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_BG_CDC_NAME);
if(state.idBgImage < 0)
{
logTaskFailed(mLog, state.idBgImage);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Load cursor from CDC ... ");
state.idCursor = mC64.cursorMgr().load(OUTPUT_CDC, RESOURCE_CURSOR_CDC_NAME);
Cursor* pCursor = mC64.cursorMgr().get(state.idCursor);
if(state.idCursor < 0 || pCursor == nullptr)
{
logTaskFailed(mLog, state.idCursor);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Select cursor ... ");
if(mC64.cursorMgr().select(state.idCursor) < 0 || !mC64.cursorMgr().show())
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Load built-in font ... ");
state.idBuiltInFont = mC64.fontMgr().getBuiltin("CourierNew10White");
if(state.idBuiltInFont < 0 || mC64.fontMgr().get(state.idBuiltInFont) == nullptr)
{
logTaskFailed(mLog, state.idBuiltInFont);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, "\nPARTICLES\n");
mLog.msg(LL_INFO, " Create emitter ... ");
state.idEmitter = mC64.particleMgr().create("TutorialMouseEmitter");
state.idCollisionSet = mC64.physics().createCollisionSet("TutorialParticleCollider");
const Sint32 idCollider = mC64.physics().addColliderRectangle(state.idCollisionSet,
TUTORIAL_COLLIDER_X, TUTORIAL_COLLIDER_Y, TUTORIAL_COLLIDER_W, TUTORIAL_COLLIDER_H);
if(state.idEmitter <= 0 || state.idCollisionSet <= 0 || idCollider <= 0
|| getEmitter(state) == nullptr || !applyPreset(state, 0))
{
logTaskFailed(mLog, state.idEmitter);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, "\nTOOLS: debug window and console\n");
mLog.msg(LL_INFO, " Create debug window ... ");
state.idDebugWindow = mC64.guiMgr().create("Debug", CRM64PRO_GUI_DEBUGWINDOW);
DebugWindow* pDebug = mC64.guiMgr().getDebugWindow(state.idDebugWindow);
if(!pDebug)
{
logTaskFailed(mLog, state.idDebugWindow);
return -1;
}
pDebug->addWatch("Mouse X", nullptr, &state.fMouseX);
pDebug->addWatch("Mouse Y", nullptr, &state.fMouseY);
pDebug->addWatch("Preset", &state.iPreset);
pDebug->addWatch("Render fps", &state.iCurrentRenderRate);
pDebug->addWatch("Logic fps", &state.iCurrentLogicRate);
pDebug->addWatch("Logic frames", &state.iLogicFrames);
pDebug->baseWidget().hide();
logTaskOk(mLog);
Console* pConsole = mC64.guiMgr().getConsole();
if(pConsole) pConsole->print("Tutorial 10: Particles ready. Press ` to toggle the console.\n");
mLog.msg(LL_INFO, "\nCONTROLS\n");
mLog.msg(LL_INFO, " [Q] / [ESC] Quit\n");
mLog.msg(LL_INFO, " [Mouse] Move particle emitter\n");
mLog.msg(LL_INFO, " [Left Click] Spawn explosion one-shot\n");
mLog.msg(LL_INFO, " [1..5] Switch preset\n");
mLog.msg(LL_INFO, " [O] Cycle collider mode: NONE / KILL / STOP / BOUNCE\n");
mLog.msg(LL_INFO, " [D] Toggle debug window\n");
mLog.msg(LL_INFO, " [`] Toggle console\n");
mLog.msg(LL_INFO, "\nRUNNING\n");
SDL_Event event;
bool bRunning = true;
while(bRunning)
{
while(mC64.update(&event))
{
if(event.type == SDL_EVENT_QUIT)
{
bRunning = false;
}
else if(event.type == SDL_EVENT_KEY_DOWN)
{
if(event.key.key == SDLK_Q || event.key.key == SDLK_ESCAPE) bRunning = false;
else if(event.key.key >= SDLK_1 && event.key.key <= SDLK_5)
{
const Sint32 iPreset = static_cast<Sint32>(event.key.key - SDLK_1);
if(applyPreset(state, iPreset))
mLog.msg(LL_INFO, " Preset: %s\n", g_Presets[state.iPreset].szName);
else
mLog.msg(LL_ERROR, " Preset change failed\n");
}
else if(event.key.key == SDLK_D)
{
toggleDebugWindow(pDebug);
}
else if(event.key.key == SDLK_O && cycleCollisionMode(state))
{
mLog.msg(LL_INFO, " Collider: %s\n", getCollisionModeName(state.eCollisionMode));
}
}
else if(event.type == SDL_EVENT_MOUSE_MOTION)
{
state.fMouseX = event.motion.x;
state.fMouseY = event.motion.y;
}
else if(event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT)
{
const Sint32 idOneShot = mC64.particleMgr().playOneShot(
PEP_EXPLOSION, event.button.x, event.button.y);
ParticleEmitter* pOneShot = mC64.particleMgr().get(idOneShot);
if(pOneShot)
{
pOneShot->setCollisionSet(state.idCollisionSet);
pOneShot->setCollisionMode(state.eCollisionMode, 0.75f);
}
}
}
if(!bRunning) break;
state.fMouseX = mC64.cursorMgr().getX();
state.fMouseY = mC64.cursorMgr().getY();
state.iCurrentRenderRate = static_cast<Sint32>(mC64.timer().getCurrentRFR());
state.iCurrentLogicRate = static_cast<Sint32>(mC64.timer().getCurrentLFR());
updateLogic(state, mC64.getLogicDeltaTime());
}
mC64.particleMgr().info();
mLog.msg(LL_INFO, "\nCLEANUP\n");
mLog.msg(LL_INFO, " Close particle emitters ... ");
mC64.particleMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close particle collider ... ");
if(mC64.physics().closeCollisionSet(state.idCollisionSet) < 0) logTaskFailed(mLog);
else logTaskOk(mLog);
mLog.msg(LL_INFO, " Close debug window ... ");
mC64.guiMgr().close(state.idDebugWindow);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close fonts ... ");
mC64.fontMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close cursors ... ");
mC64.cursorMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close images ... ");
mC64.imageMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close CDC archives ... ");
mC64.archiveMgr().close(0);
logTaskOk(mLog);
return 0;
}
Sint32 close(Sint32 idCDC) override
Close and destroy a CDC object.
Definition CoreArchive.cpp:2476
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
Console Object class.
Definition CRM64Pro.h:9678
Cursor Object class.
Definition CRM64Pro.h:3734
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
bool show()
Make visible the selected mouse cursor.
Definition GraphicsCursor.cpp:1001
Sint32 load(const string &sCDCFile, const string &sName)
Load a cursor stored in a CDC file.
Definition GraphicsCursor.cpp:1115
Sint32 close(Sint32 idCursor) override
Close and destroy a cursor.
Definition GraphicsCursor.cpp:897
Cursor * get(Sint32 idCursor)
Get a pointer to the cursor using its handle.
Definition GraphicsCursor.cpp:962
float getX() const
Get the mouse cursor X position.
Definition GraphicsCursor.cpp:1035
DebugWindow Object class.
Definition CRM64Pro.h:9810
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
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
Log Object class.
Definition CRM64Pro.h:771
CRM64Pro GDK Main class.
Definition CRM64Pro.h:502
Particle emitter object class.
Definition CRM64Pro.h:6605
bool setCollisionMode(eParticleCollisionMode eMode, float fBounciness=0.5f)
Set persistent collision behavior.
Definition GraphicsParticleEffect.cpp:484
bool setPosition(float fX, float fY)
Set emitter world position.
Definition GraphicsParticleEffect.cpp:690
bool play()
Start or resume particle emission.
Definition GraphicsParticleEffect.cpp:159
bool setMaxParticles(Sint32 iMaxParticles)
Set maximum live particle count.
Definition GraphicsParticleEffect.cpp:357
bool setCollisionSet(Sint32 idCollisionSet)
Bind a runtime Physics collision set.
Definition GraphicsParticleEffect.cpp:509
Sint32 getLiveCount() const
Get current live particle count.
Definition GraphicsParticleEffect.cpp:749
bool applyPreset(eParticleEffectPreset ePreset)
Apply a built-in particle preset.
Definition GraphicsParticleEffect.cpp:672
Sint32 playOneShot(eParticleEffectPreset ePreset, float fX, float fY)
Create and play a temporary one-shot preset emitter.
Definition GraphicsParticleEffectSimulation.cpp:1716
bool info(Sint32 iMode=0) override
Request Particle Manager information.
Definition GraphicsParticleEffect.cpp:1030
Sint32 render(Sint32 idRes=0)
Render all loaded ParticleEmitter objects.
Definition GraphicsParticleEffectRender.cpp:1032
Sint32 create(const string &sName, Uint32 iVersion=10) override
Create a new ParticleEmitter.
Definition GraphicsParticleEffect.cpp:1072
ParticleEmitter * get(Sint32 idParticle)
Get a ParticleEmitter pointer.
Definition GraphicsParticleEffect.cpp:1181
Sint32 close(Sint32 idParticle) override
Close and destroy a ParticleEmitter.
Definition GraphicsParticleEffect.cpp:1115
Sint32 addColliderRectangle(Sint32 idCollisionSet, float fX, float fY, float fWidth, float fHeight)
Add an axis-aligned rectangle collider.
Definition GraphicsPhysics.cpp:658
Sint32 createCollisionSet(const string &sName)
Create a persistent collision set.
Definition GraphicsPhysics.cpp:912
Sint32 closeCollisionSet(Sint32 idCollisionSet)
Close and destroy a collision set.
Definition GraphicsPhysics.cpp:934
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_NORMAL
Collect allocations per module (CRM64Pro and user). Default for debug builds.
Definition CRM64Pro.h:386
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 line(float fX1, float fY1, float fX2, float fY2, Uint32 iColor, Sint32 idRes=0)
Draw a line.
Definition GraphicsGFX.cpp:2245
bool drawGlow(float fX, float fY, float fRadius, Uint32 iColor, float fIntensity=1.0f, Sint32 idRes=0)
Draw a soft additive glow.
Definition GraphicsGFX.cpp:2868
Sint32 create(const string &sName, Uint32 iType=CRM64PRO_GUI_PANEL) override
Creates a new GUI object.
Definition GUI.cpp:1051
Sint32 addWatch(const string &sName, Sint32 *iIntegerVar, float *fFloatVar=nullptr)
Add a watch.
Definition GUIDebugWindow.cpp:275
#define CRM64PRO_GUI_DEBUGWINDOW
Definition CRM64Pro.h:9195
Widget & baseWidget()
Get the reference to base Widget for this object.
Definition GUIDebugWindow.cpp:269
Sint32 print(const char *format,...)
Print a formatted message to the console.
Definition GUIConsole.cpp:403
Console * getConsole(Sint32 idGUI=CRM64PRO_GUI_CONSOLE)
Get a pointer to a Console using its handle.
Definition GUI.cpp:1366
DebugWindow * getDebugWindow(Sint32 idGUI)
Get a pointer to a DebugWindow using its handle.
Definition GUI.cpp:1375
Sint32 close(Sint32 idGUI) override
Close and destroy a GUI object.
Definition GUI.cpp:1168
eGeneralStatus status() const
Get the widget status.
Definition GUIWidget.cpp:1772
bool hide()
Hide a widget.
Definition GUIWidget.cpp:1729
bool show()
Show a widget.
Definition GUIWidget.cpp:1671
Sint32 setPosition(const Position &rPosX=Position(PH_CENTER), const Position &rPosY=Position(PH_CENTER))
Set the position.
Definition GUIWidget.cpp:1923
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
float getLogicDeltaTime() const
Get the delta time that must be consumed by the current logic step.
Definition CoreMain.cpp:678
GUIMgr & guiMgr()
Get reference to GUI Manager.
Definition CoreMain.cpp:808
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
Physics & physics()
Get reference to Physics module.
Definition CoreMain.cpp:790
CursorMgr & cursorMgr()
Get reference to Cursor Manager.
Definition CoreMain.cpp:760
ParticleMgr & particleMgr()
Get reference to Particle Manager.
Definition CoreMain.cpp:802
GFX & gfx()
Get reference to GFX module.
Definition CoreMain.cpp:784
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
ArchiveMgr & archiveMgr()
Get reference to Archive Manager.
Definition CoreMain.cpp:736
FontMgr & fontMgr()
Get reference to Font Manager.
Definition CoreMain.cpp:778
ConfigMgr & configMgr()
Get reference to Config Manager.
Definition CoreMain.cpp:712
@ GS_SHOWN
General status: enabled and shown.
Definition CRM64Pro.h:300
@ PH_RIGHT
Widget/Font: right with optional offset. Scene: align to map right/end (MapWidth - ViewportWidth)....
Definition CRM64Pro.h:340
@ PH_TOP
Widget/Font: top with optional offset. Scene: align to map top/begin (0.0). Valid for Y axis.
Definition CRM64Pro.h:337
eParticleCollisionMode
Particle collision behavior.
Definition CRM64Pro.h:6520
eParticleEffectPreset
Particle effect presets.
Definition CRM64Pro.h:6529
@ PCM_KILL
Kill particle on first collision.
Definition CRM64Pro.h:6524
@ PCM_BOUNCE
Bounce on collision and keep alive.
Definition CRM64Pro.h:6522
@ PCM_NONE
No particle collision.
Definition CRM64Pro.h:6521
@ PCM_STOP
Stop movement after first collision while lifetime, animation and death processing continue.
Definition CRM64Pro.h:6523
@ PEP_SNOW
Slow soft drifting snow field.
Definition CRM64Pro.h:6542
@ PEP_FIRE
Additive flame plume.
Definition CRM64Pro.h:6536
@ PEP_SMOKE
Rising grey smoke column.
Definition CRM64Pro.h:6532
@ PEP_SPARKS
Continuous fast line sparks.
Definition CRM64Pro.h:6539
@ PEP_EXPLOSION
One-shot radial explosion burst.
Definition CRM64Pro.h:6548
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
CRM64Pro GDK.
Definition AudioTrack.cpp:36
Absolute coordinate or helper-relative position used by layout APIs.
Definition CRM64Pro.h:346

Navigation

Previous: Tutorial 09 - Audio tracks

Back to the tutorials index

Continue with Tutorial 11: 2D lightmaps