Playable scripted NinjaSnowWar.
Disabled garbage collection from CScriptArray again because of some very odd "objects not getting deleted" bugs. Unprepare script contexts before garbage collection.
This commit is contained in:
parent
c73c30fde1
commit
bb67b6d7fe
@ -45,7 +45,7 @@
|
||||
<EnemySpawnRate>1</EnemySpawnRate>
|
||||
<EnemySpawnPosition>0 1000 12000</EnemySpawnPosition>
|
||||
<EnemySpawnOffset>4000</EnemySpawnOffset>
|
||||
<EnemySpawnVelocity>0 1000 -2500</EnemySpawnVelocity>
|
||||
<EnemySpawnVelocity>0 1000 -3000</EnemySpawnVelocity>
|
||||
<PowerUpSpawnRate>15</PowerUpSpawnRate>
|
||||
<PowerUpSpawnOffset>4000</PowerUpSpawnOffset>
|
||||
<PowerUpSpawnHeight>5000</PowerUpSpawnHeight>
|
||||
|
174
Bin/Data/Scripts/AIController.as
Normal file
174
Bin/Data/Scripts/AIController.as
Normal file
@ -0,0 +1,174 @@
|
||||
const float initialAggression = 0.0025;
|
||||
const float initialPrediction = 3000;
|
||||
const float initialAimSpeed = 5;
|
||||
const float deltaAggression = 0.00005;
|
||||
const float deltaPrediction = -20;
|
||||
const float deltaAimSpeed = 0.15;
|
||||
const float maxAggression = 0.01;
|
||||
const float maxPrediction = 2000;
|
||||
const float maxAimSpeed = 20;
|
||||
|
||||
float aiAggression = initialAggression;
|
||||
float aiPrediction = initialPrediction;
|
||||
float aiAimSpeed = initialAimSpeed;
|
||||
|
||||
void resetAI()
|
||||
{
|
||||
aiAggression = initialAggression;
|
||||
aiPrediction = initialPrediction;
|
||||
aiAimSpeed = initialAimSpeed;
|
||||
}
|
||||
|
||||
void makeAIHarder()
|
||||
{
|
||||
aiAggression += deltaAggression;
|
||||
if (aiAggression > maxAggression)
|
||||
aiAggression = maxAggression;
|
||||
|
||||
aiPrediction += deltaPrediction;
|
||||
if (aiPrediction < maxPrediction)
|
||||
aiPrediction = maxPrediction;
|
||||
|
||||
aiAimSpeed += deltaAimSpeed;
|
||||
if (aiAimSpeed > maxAimSpeed)
|
||||
aiAimSpeed = maxAimSpeed;
|
||||
}
|
||||
|
||||
class AIController
|
||||
{
|
||||
void control(Ninja@ ninja, Entity@ entity, float timeStep)
|
||||
{
|
||||
Scene@ scene = entity.getScene();
|
||||
RigidBody@ body = entity.getComponent("RigidBody");
|
||||
|
||||
// Get closest ninja on the player's side
|
||||
Entity@ targetEntity;
|
||||
Ninja@ targetNinja;
|
||||
RigidBody@ targetBody;
|
||||
array<Entity@> entities = scene.getScriptedEntities("Ninja");
|
||||
float closestDistance = M_INFINITY;
|
||||
for (uint i = 0; i < entities.length(); ++i)
|
||||
{
|
||||
Ninja@ otherNinja = cast<Ninja>(entities[i].getScriptObject());
|
||||
RigidBody@ otherBody = entities[i].getComponent("RigidBody");
|
||||
if (otherNinja.side == SIDE_PLAYER)
|
||||
{
|
||||
float distance = (body.getPhysicsPosition() - otherBody.getPhysicsPosition()).getLength();
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
@targetEntity = entities[i];
|
||||
@targetNinja = otherNinja;
|
||||
@targetBody = otherBody;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((@targetEntity != null) && (targetNinja.health > 0))
|
||||
{
|
||||
ninja.controls.set(CTRL_FIRE, false);
|
||||
ninja.controls.set(CTRL_JUMP, false);
|
||||
|
||||
float deltaX = 0.0f;
|
||||
float deltaY = 0.0f;
|
||||
|
||||
// Aim from own head to target's feet
|
||||
Vector3 ownPos(body.getPhysicsPosition() + Vector3(0, 90, 0));
|
||||
Vector3 targetPos(targetBody.getPhysicsPosition() + Vector3(0, -90, 0));
|
||||
float distance = (targetPos - ownPos).getLength();
|
||||
|
||||
// Use prediction according to target distance & estimated snowball speed
|
||||
Vector3 currentAim(ninja.getAim() * Vector3(0, 0, 1));
|
||||
float predictDistance = distance;
|
||||
if (predictDistance > 5000) predictDistance = 5000;
|
||||
Vector3 predictedPos = targetPos + targetBody.getLinearVelocity() * predictDistance / aiPrediction;
|
||||
Vector3 targetAim = (predictedPos - ownPos);
|
||||
|
||||
// Add distance/height compensation
|
||||
float compensation = max(targetAim.getLength() - 1500, 0);
|
||||
targetAim += Vector3(0, 0.005, 0) * (compensation * compensation);
|
||||
|
||||
// X-aiming
|
||||
targetAim.normalize();
|
||||
Vector3 currentYaw(currentAim.x, 0, currentAim.z);
|
||||
Vector3 targetYaw(targetAim.x, 0, targetAim.z);
|
||||
currentYaw.normalize();
|
||||
targetYaw.normalize();
|
||||
deltaX = clamp(Quaternion(currentYaw, targetYaw).getYaw(), -aiAimSpeed, aiAimSpeed);
|
||||
|
||||
// Y-aiming
|
||||
Vector3 currentPitch(0, currentAim.y, 1);
|
||||
Vector3 targetPitch(0, targetAim.y, 1);
|
||||
currentPitch.normalize();
|
||||
targetPitch.normalize();
|
||||
deltaY = clamp(Quaternion(currentPitch, targetPitch).getPitch(), -aiAimSpeed, aiAimSpeed);
|
||||
|
||||
ninja.controls.yaw += 0.1 * deltaX;
|
||||
ninja.controls.pitch += 0.1 * deltaY;
|
||||
|
||||
// Firing? if close enough and relatively correct aim
|
||||
if ((distance < 2500) && (currentAim.dotProduct(targetAim) > 0.75))
|
||||
{
|
||||
if (random(1.0) < aiAggression)
|
||||
ninja.controls.set(CTRL_FIRE, true);
|
||||
}
|
||||
|
||||
// Movement
|
||||
ninja.dirChangeTime -= timeStep;
|
||||
if (ninja.dirChangeTime <= 0)
|
||||
{
|
||||
ninja.dirChangeTime = 0.5 + random(1.0);
|
||||
ninja.controls.set(CTRL_UP|CTRL_DOWN|CTRL_LEFT|CTRL_RIGHT, false);
|
||||
|
||||
// Far distance: go forward
|
||||
if (distance > 3000)
|
||||
ninja.controls.set(CTRL_UP, true);
|
||||
else if (distance > 600)
|
||||
{
|
||||
// Medium distance: random strafing, predominantly forward
|
||||
float v = random(1.0);
|
||||
if (v < 0.8)
|
||||
ninja.controls.set(CTRL_UP, true);
|
||||
float h = random(1.0);
|
||||
if (h < 0.3)
|
||||
ninja.controls.set(CTRL_LEFT, true);
|
||||
if (h > 0.7)
|
||||
ninja.controls.set(CTRL_RIGHT, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close distance: random strafing backwards
|
||||
float v = random(1.0);
|
||||
if (v < 0.8)
|
||||
ninja.controls.set(CTRL_DOWN, true);
|
||||
float h = random(1.0);
|
||||
if (h < 0.4)
|
||||
ninja.controls.set(CTRL_LEFT, true);
|
||||
if (h > 0.6)
|
||||
ninja.controls.set(CTRL_RIGHT, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Random jump, if going forward
|
||||
if ((ninja.controls.isDown(CTRL_UP)) && (distance < 1000))
|
||||
{
|
||||
if (random(1.0) < (aiAggression / 5.0))
|
||||
ninja.controls.set(CTRL_JUMP, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no target, walk idly
|
||||
ninja.controls.set(CTRL_ALL, false);
|
||||
ninja.controls.set(CTRL_UP, true);
|
||||
ninja.dirChangeTime -= timeStep;
|
||||
if (ninja.dirChangeTime <= 0)
|
||||
{
|
||||
ninja.dirChangeTime = 1 + random(2.0);
|
||||
ninja.controls.yaw += 0.1 * (random(600.0) - 300.0);
|
||||
}
|
||||
if (ninja.isSliding)
|
||||
ninja.controls.yaw += 0.2;
|
||||
}
|
||||
}
|
||||
}
|
@ -12,8 +12,6 @@ const int SIDE_ENEMY = 2;
|
||||
|
||||
class GameObject : ScriptObject
|
||||
{
|
||||
Controls controls;
|
||||
Controls prevControls;
|
||||
bool onGround;
|
||||
bool isSliding;
|
||||
float duration;
|
||||
@ -37,11 +35,6 @@ class GameObject : ScriptObject
|
||||
{
|
||||
}
|
||||
|
||||
void setControls(const Controls&in newControls)
|
||||
{
|
||||
controls = newControls;
|
||||
}
|
||||
|
||||
void updateFixed(float timeStep)
|
||||
{
|
||||
// Disappear when duration expired
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "Scripts/GameObject.as"
|
||||
#include "Scripts/AIController.as"
|
||||
|
||||
const int ANIM_MOVE = 1;
|
||||
const int ANIM_ATTACK = 2;
|
||||
@ -14,9 +15,13 @@ const Vector3 ninjaThrowPosition(0, 20, 100);
|
||||
const float ninjaThrowDelay = 0.1;
|
||||
const float ninjaDrawDistance = 15000;
|
||||
const float ninjaCorpseDuration = 3;
|
||||
const int ninjaPoints = 250;
|
||||
|
||||
class Ninja : GameObject
|
||||
{
|
||||
Controls controls;
|
||||
Controls prevControls;
|
||||
AIController@ controller;
|
||||
bool okToJump;
|
||||
bool smoke;
|
||||
float inAirTime;
|
||||
@ -101,6 +106,10 @@ class Ninja : GameObject
|
||||
return;
|
||||
}
|
||||
|
||||
// AI control if controller exists
|
||||
if (@controller != null)
|
||||
controller.control(this, entity, timeStep);
|
||||
|
||||
RigidBody@ body = entity.getComponent("RigidBody");
|
||||
AnimationController@ controller = entity.getComponent("AnimationController");
|
||||
|
||||
@ -275,6 +284,12 @@ class Ninja : GameObject
|
||||
deathDir = 1;
|
||||
|
||||
playSound("Sounds/SmallExplosion.wav");
|
||||
|
||||
VariantMap eventData;
|
||||
eventData["Points"] = ninjaPoints;
|
||||
eventData["DamageSide"] = lastDamageSide;
|
||||
sendEvent("Points", eventData);
|
||||
sendEvent("Kill", eventData);
|
||||
}
|
||||
|
||||
deathTime += timeStep;
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Partial remake of NinjaSnowWar in script
|
||||
// Remake of NinjaSnowWar in script
|
||||
// Does not support load/save, or multiplayer yet.
|
||||
|
||||
#include "Scripts/LightFlash.as"
|
||||
#include "Scripts/Ninja.as"
|
||||
@ -10,7 +11,13 @@ const float mouseSensitivity = 0.125;
|
||||
const float cameraMinDist = 25;
|
||||
const float cameraMaxDist = 500;
|
||||
const float cameraSafetyDist = 30;
|
||||
const int initialMaxEnemies = 5;
|
||||
const int finalMaxEnemies = 25;
|
||||
const int maxPowerups = 5;
|
||||
const int incrementEach = 10;
|
||||
const int playerHealth = 20;
|
||||
const float enemySpawnRate = 1;
|
||||
const float powerupSpawnRate = 15;
|
||||
|
||||
Scene@ gameScene;
|
||||
Camera@ gameCamera;
|
||||
@ -21,8 +28,15 @@ BorderImage@ healthBar;
|
||||
BorderImage@ sight;
|
||||
|
||||
Controls playerControls;
|
||||
Controls prevPlayerControls;
|
||||
bool paused = false;
|
||||
bool gameOn = false;
|
||||
int score = 0;
|
||||
int hiscore = 0;
|
||||
int maxEnemies = 0;
|
||||
int incrementCounter = 0;
|
||||
float enemySpawnTimer = 0;
|
||||
float powerupSpawnTimer = 0;
|
||||
|
||||
void start()
|
||||
{
|
||||
@ -34,7 +48,10 @@ void start()
|
||||
startGame();
|
||||
|
||||
subscribeToEvent("Update", "handleUpdate");
|
||||
subscribeToEvent("PhysicsPreStep", "handleFixedUpdate");
|
||||
subscribeToEvent("PostUpdate", "handlePostUpdate");
|
||||
subscribeToEvent("Points", "handlePoints");
|
||||
subscribeToEvent("Kill", "handleKill");
|
||||
subscribeToEvent("KeyDown", "handleKeyDown");
|
||||
subscribeToEvent("WindowResized", "handleWindowResized");
|
||||
}
|
||||
@ -155,16 +172,29 @@ void startGame()
|
||||
{
|
||||
// Clear the scene of all existing scripted entities
|
||||
array<Entity@> scriptedEntities = gameScene.getScriptedEntities();
|
||||
for (int i = 0; i < scriptedEntities.length(); ++i)
|
||||
for (uint i = 0; i < scriptedEntities.length(); ++i)
|
||||
gameScene.removeEntity(scriptedEntities[i]);
|
||||
|
||||
Entity@ playerEntity = gameScene.createEntity("Player");
|
||||
GameObject@ object = cast<GameObject>(playerEntity.createScriptObject("Scripts/NinjaSnowWar.as", "Ninja"));
|
||||
object.create(Vector3(0, 90, 0), Quaternion());
|
||||
object.health = object.maxHealth = playerHealth;
|
||||
object.side = SIDE_PLAYER;
|
||||
Ninja@ playerNinja = cast<Ninja>(playerEntity.createScriptObject("Scripts/NinjaSnowWar.as", "Ninja"));
|
||||
playerNinja.create(Vector3(0, 90, 0), Quaternion());
|
||||
playerNinja.health = playerNinja.maxHealth = playerHealth;
|
||||
playerNinja.side = SIDE_PLAYER;
|
||||
// Make sure the player can not shoot on first frame by holding the button down
|
||||
playerNinja.controls = playerNinja.prevControls = playerControls;
|
||||
|
||||
resetAI();
|
||||
|
||||
gameOn = true;
|
||||
score = 0;
|
||||
maxEnemies = initialMaxEnemies;
|
||||
incrementCounter = 0;
|
||||
enemySpawnTimer = 0;
|
||||
powerupSpawnTimer = 0;
|
||||
playerControls.yaw = 0;
|
||||
playerControls.pitch = 0;
|
||||
|
||||
messageText.setText("");
|
||||
}
|
||||
|
||||
void handleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
@ -174,7 +204,7 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
if (input.getKeyPress(KEY_F2))
|
||||
engine.setDebugDrawMode(engine.getDebugDrawMode() ^ DEBUGDRAW_PHYSICS);
|
||||
|
||||
if ((!console.isVisible()) && (input.getKeyPress('P')))
|
||||
if ((!console.isVisible()) && (input.getKeyPress('P')) && (gameOn))
|
||||
{
|
||||
paused = !paused;
|
||||
if (paused)
|
||||
@ -187,20 +217,122 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
updateControls();
|
||||
}
|
||||
|
||||
void handleFixedUpdate(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
// Check that scene being updated matches (we have only one scene, but for completeness...)
|
||||
if (@eventData["Scene"].getScene() != @gameScene)
|
||||
return;
|
||||
|
||||
float timeStep = eventData["TimeStep"].getFloat();
|
||||
|
||||
// Spawn new objects and check for end/restart of game
|
||||
spawnObjects(timeStep);
|
||||
checkEndAndRestart();
|
||||
}
|
||||
|
||||
void handlePostUpdate(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
updateCamera();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void handlePoints(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
if (eventData["DamageSide"].getInt() == SIDE_PLAYER)
|
||||
{
|
||||
score += eventData["Points"].getInt();
|
||||
if (score > hiscore)
|
||||
hiscore = score;
|
||||
}
|
||||
}
|
||||
|
||||
void handleKill(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
if (eventData["DamageSide"].getInt() == SIDE_PLAYER)
|
||||
{
|
||||
makeAIHarder();
|
||||
|
||||
// Increment amount of simultaneous enemies after enough kills
|
||||
incrementCounter++;
|
||||
if (incrementCounter >= incrementEach)
|
||||
{
|
||||
incrementCounter = 0;
|
||||
if (maxEnemies < finalMaxEnemies)
|
||||
maxEnemies++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spawnObjects(float timeStep)
|
||||
{
|
||||
// Spawn powerups
|
||||
powerupSpawnTimer += timeStep;
|
||||
if (powerupSpawnTimer >= powerupSpawnRate)
|
||||
{
|
||||
powerupSpawnTimer = 0;
|
||||
//int numPowerups = gameScene.getScriptedEntities("SnowCrate").length() + gameScene.getScriptedEntities("Potion").length();
|
||||
int numPowerups = 0;
|
||||
|
||||
if (numPowerups < maxPowerups)
|
||||
{
|
||||
const float maxOffset = 4000;
|
||||
float xOffset = random(maxOffset * 2.0f) - maxOffset;
|
||||
float zOffset = random(maxOffset * 2.0f) - maxOffset;
|
||||
|
||||
Vector3 position(xOffset, 5000, zOffset);
|
||||
Entity@ crateEntity = gameScene.createEntity();
|
||||
GameObject@ crateObject = cast<GameObject>(crateEntity.createScriptObject("Scripts/NinjaSnowWar.as", "SnowCrate"));
|
||||
crateObject.create(position, Quaternion());
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn enemies
|
||||
enemySpawnTimer += timeStep;
|
||||
if (enemySpawnTimer > enemySpawnRate)
|
||||
{
|
||||
enemySpawnTimer = 0;
|
||||
// Take the player ninja into account
|
||||
int numEnemies = gameScene.getScriptedEntities("Ninja").length() - 1;
|
||||
|
||||
if (numEnemies < maxEnemies)
|
||||
{
|
||||
const float maxOffset = 4000;
|
||||
float offset = random(maxOffset * 2.0) - maxOffset;
|
||||
// Random north/east/south/west direction
|
||||
int dir = randomInt() & 3;
|
||||
dir *= 90;
|
||||
Quaternion q(dir, Vector3(0, 1, 0));
|
||||
Vector3 position(q * Vector3(offset, 1000, 12000));
|
||||
|
||||
Entity@ enemyEntity = gameScene.createEntity();
|
||||
Ninja@ enemyNinja = cast<Ninja>(enemyEntity.createScriptObject("Scripts/NinjaSnowWar.as", "Ninja"));
|
||||
enemyNinja.create(position, q);
|
||||
enemyNinja.side = SIDE_ENEMY;
|
||||
@enemyNinja.controller = AIController();
|
||||
RigidBody@ enemyBody = enemyEntity.getComponent("RigidBody");
|
||||
enemyBody.setLinearVelocity(q * Vector3(0, 1000, -3000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkEndAndRestart()
|
||||
{
|
||||
if ((gameOn) && (@gameScene.getEntity("Player") == null))
|
||||
{
|
||||
gameOn = false;
|
||||
messageText.setText("Press Fire or Jump to restart!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!gameOn) && (playerControls.isPressed(CTRL_FIRE | CTRL_JUMP, prevPlayerControls)))
|
||||
startGame();
|
||||
}
|
||||
|
||||
void updateControls()
|
||||
{
|
||||
prevPlayerControls = playerControls;
|
||||
playerControls.set(CTRL_ALL, false);
|
||||
|
||||
Entity@ playerEntity = gameScene.getEntity("Player");
|
||||
if (@playerEntity == null)
|
||||
return;
|
||||
|
||||
if (!console.isVisible())
|
||||
{
|
||||
if (input.getKeyDown('W'))
|
||||
@ -226,11 +358,12 @@ void updateControls()
|
||||
playerControls.pitch += mouseSensitivity * input.getMouseMoveY();
|
||||
playerControls.pitch = clamp(playerControls.pitch, -60, 60);
|
||||
|
||||
GameObject@ object = cast<GameObject>(playerEntity.getScriptObject());
|
||||
object.setControls(playerControls);
|
||||
|
||||
if ((input.getKeyPress('K')) && (object.health > 0))
|
||||
object.health = object.health - 1;
|
||||
Entity@ playerEntity = gameScene.getEntity("Player");
|
||||
if (@playerEntity != null)
|
||||
{
|
||||
Ninja@ playerNinja = cast<Ninja>(playerEntity.getScriptObject());
|
||||
playerNinja.controls = playerControls;
|
||||
}
|
||||
}
|
||||
|
||||
void updateCamera()
|
||||
@ -268,6 +401,9 @@ void updateStatus()
|
||||
if (engine.isHeadless())
|
||||
return;
|
||||
|
||||
scoreText.setText("Score " + score);
|
||||
hiscoreText.setText("Hiscore " + hiscore);
|
||||
|
||||
Entity@ playerEntity = gameScene.getEntity("Player");
|
||||
if (@playerEntity == null)
|
||||
return;
|
||||
|
@ -4,6 +4,7 @@ const int snowcrateHealth = 5;
|
||||
const float snowcrateMass = 200;
|
||||
const float snowcrateFriction = 0.35;
|
||||
const float snowcrateDrawDistance = 15000;
|
||||
const int snowcratePoints = 250;
|
||||
|
||||
class SnowCrate : GameObject
|
||||
{
|
||||
@ -47,6 +48,11 @@ class SnowCrate : GameObject
|
||||
spawnParticleEffect(body.getPhysicsPosition(), "Particle/SnowExplosionBig.xml", 2);
|
||||
spawnObject(body.getPhysicsPosition(), Quaternion(), "Potion");
|
||||
scene.removeEntity(entity);
|
||||
|
||||
VariantMap eventData;
|
||||
eventData["Points"] = snowcratePoints;
|
||||
eventData["DamageSide"] = lastDamageSide;
|
||||
sendEvent("Points", eventData);
|
||||
}
|
||||
}
|
||||
}
|
@ -95,19 +95,8 @@ Engine::Engine(const std::string& windowTitle, const std::string& logFileName, b
|
||||
|
||||
mCache = new ResourceCache();
|
||||
|
||||
// Register the inbuilt events as local only
|
||||
static const std::string inbuiltEvents[] = {
|
||||
"Update", "PostUpdate", "PostRenderUpdate", "ClientIdentity", "ClientJoinedScene", "ClientLeftScene", "ClientControls",
|
||||
"ClientDisconnected", "GotSceneInfoList", "JoinedScene", "JoinSceneFailed", "FileTransferCompleted", "FileTransferFailed",
|
||||
"ControlsUpdate", "ControlsPlayback", "MouseButtonDown", "MouseButtonUp", "MouseMove", "KeyDown", "KeyUp", "Char",
|
||||
"PeerConnected", "NetworkPacket", "PeerDisconnected", "PhysicsPreStep", "PhysicsPostStep", "PhysicsCollision",
|
||||
"EntityCollision", "WindowMessage", "WindowResized", "BeginFrame", "EndFrame", "SceneUpdate", "ScenePostUpdate",
|
||||
"AsyncLoadProgress", "AsyncLoadFinished", "TryFocus", "Focused", "Defocused", "Pressed", "Toggled", "SliderChanged",
|
||||
"ViewChanged", "TextChanged", "TextFinished", "ItemSelected", ""
|
||||
};
|
||||
|
||||
for (unsigned i = 0; inbuiltEvents[i].length(); ++i)
|
||||
registerLocalOnlyEvent(inbuiltEvents[i]);
|
||||
// Register the engine and library events as local only
|
||||
resetLocalOnlyEvents();
|
||||
|
||||
sInstance = this;
|
||||
}
|
||||
@ -277,9 +266,14 @@ void Engine::runFrame(Scene* scene, Camera* camera, bool updateScene)
|
||||
{
|
||||
PROFILE(Engine_RunFrame);
|
||||
|
||||
// Get frame timestep / update / render
|
||||
float timeStep = getNextTimeStep();
|
||||
update(timeStep, scene, camera, updateScene);
|
||||
render();
|
||||
|
||||
// Perform script garbage collection outside everything else
|
||||
if (mScriptEngine)
|
||||
mScriptEngine->garbageCollect(false);
|
||||
}
|
||||
|
||||
mProfiler->endFrame();
|
||||
@ -372,6 +366,24 @@ DebugHud* Engine::createDebugHud()
|
||||
return mDebugHud;
|
||||
}
|
||||
|
||||
void Engine::resetLocalOnlyEvents()
|
||||
{
|
||||
static const std::string inbuiltEvents[] = {
|
||||
"Update", "PostUpdate", "PostRenderUpdate", "ClientIdentity", "ClientJoinedScene", "ClientLeftScene", "ClientControls",
|
||||
"ClientDisconnected", "GotSceneInfoList", "JoinedScene", "JoinSceneFailed", "FileTransferCompleted", "FileTransferFailed",
|
||||
"ControlsUpdate", "ControlsPlayback", "MouseButtonDown", "MouseButtonUp", "MouseMove", "KeyDown", "KeyUp", "Char",
|
||||
"PeerConnected", "NetworkPacket", "PeerDisconnected", "PhysicsPreStep", "PhysicsPostStep", "PhysicsCollision",
|
||||
"EntityCollision", "WindowMessage", "WindowResized", "BeginFrame", "EndFrame", "SceneUpdate", "ScenePostUpdate",
|
||||
"AsyncLoadProgress", "AsyncLoadFinished", "TryFocus", "Focused", "Defocused", "Pressed", "Toggled", "SliderChanged",
|
||||
"ViewChanged", "TextChanged", "TextFinished", "ItemSelected", ""
|
||||
};
|
||||
|
||||
// First clear existing registrations, then register the inbuild events
|
||||
removeAllLocalOnlyEvents();
|
||||
for (unsigned i = 0; inbuiltEvents[i].length(); ++i)
|
||||
registerLocalOnlyEvent(inbuiltEvents[i]);
|
||||
}
|
||||
|
||||
void Engine::setDefaultScene(Scene* scene)
|
||||
{
|
||||
mDefaultScene = scene;
|
||||
|
@ -90,6 +90,8 @@ public:
|
||||
void removeClient();
|
||||
//! Remove the server subsystem
|
||||
void removeServer();
|
||||
//! Clear previous and register the default local only events
|
||||
void resetLocalOnlyEvents();
|
||||
//! Set the default scene. This will always be available as the "scene" global property to scripting
|
||||
void setDefaultScene(Scene* scene);
|
||||
//! Set minimum frames per second. If FPS goes lower than this, time will appear to slow down
|
||||
|
@ -435,6 +435,7 @@ static void registerEngine(asIScriptEngine* engine)
|
||||
engine->RegisterObjectMethod("Engine", "DebugHud@+ createDebugHud()", asMETHOD(Engine, createDebugHud), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("Engine", "void removeClient()", asMETHOD(Engine, removeClient), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("Engine", "void removeServer()", asMETHOD(Engine, removeServer), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("Engine", "void resetLocalOnlyEvents()", asMETHOD(Engine, resetLocalOnlyEvents), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("Engine", "void setDefaultScene(Scene@+)", asMETHOD(Engine, setDefaultScene), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("Engine", "void setMinFps(int)", asMETHOD(Engine, setMinFps), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("Engine", "void setMaxFps(int)", asMETHOD(Engine, setMaxFps), asCALL_THISCALL);
|
||||
|
@ -64,6 +64,9 @@ static float ATan2(float y, float x)
|
||||
|
||||
static void registerMathFunctions(asIScriptEngine* engine)
|
||||
{
|
||||
engine->RegisterGlobalProperty("const float M_INFINITY", (void*)&M_INFINITY);
|
||||
engine->RegisterGlobalProperty("const float M_EPSILON", (void*)&M_EPSILON);
|
||||
|
||||
engine->RegisterGlobalFunction("float sin(float)", asFUNCTION(Sin), asCALL_CDECL);
|
||||
engine->RegisterGlobalFunction("float cos(float)", asFUNCTION(Cos), asCALL_CDECL);
|
||||
engine->RegisterGlobalFunction("float tan(float)", asFUNCTION(Tan), asCALL_CDECL);
|
||||
|
@ -92,10 +92,15 @@ void registerLocalOnlyEvent(StringHash eventType)
|
||||
|
||||
void registerLocalOnlyEvent(const std::string& name)
|
||||
{
|
||||
registerHash(name);
|
||||
registerHash(name, false);
|
||||
localOnlyEvents.insert(StringHash(name));
|
||||
}
|
||||
|
||||
void removeAllLocalOnlyEvents()
|
||||
{
|
||||
localOnlyEvents.clear();
|
||||
}
|
||||
|
||||
bool checkRemoteEvent(StringHash eventType)
|
||||
{
|
||||
return localOnlyEvents.find(eventType) == localOnlyEvents.end();
|
||||
|
@ -89,6 +89,8 @@ struct RemoteEvent
|
||||
void registerLocalOnlyEvent(StringHash eventType);
|
||||
//! Register an event to be only sent locally
|
||||
void registerLocalOnlyEvent(const std::string& name);
|
||||
//! Remove all registered local only events
|
||||
void removeAllLocalOnlyEvents();
|
||||
//! Check if an event is allowed to be sent remotely
|
||||
bool checkRemoteEvent(StringHash eventType);
|
||||
//! Return remote event sender connection. Only non-null during the event handling
|
||||
|
@ -179,8 +179,8 @@ CScriptArray::CScriptArray(asUINT length, asIObjectType* ot)
|
||||
CreateBuffer(&buffer, length);
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if (objType->GetFlags() & asOBJ_GC)
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType->GetTypeId());
|
||||
//if (objType->GetFlags() & asOBJ_GC)
|
||||
// objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType->GetTypeId());
|
||||
}
|
||||
|
||||
CScriptArray::CScriptArray(asUINT length, void *defVal, asIObjectType *ot)
|
||||
@ -211,8 +211,8 @@ CScriptArray::CScriptArray(asUINT length, void *defVal, asIObjectType *ot)
|
||||
CreateBuffer(&buffer, length);
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if (objType->GetFlags() & asOBJ_GC)
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType->GetTypeId());
|
||||
//if (objType->GetFlags() & asOBJ_GC)
|
||||
// objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType->GetTypeId());
|
||||
|
||||
// Initialize the elements with the default value
|
||||
for (asUINT n = 0; n < GetSize(); ++n)
|
||||
@ -519,27 +519,27 @@ void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src)
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptArray::EnumReferences(asIScriptEngine* engine)
|
||||
{
|
||||
// If the array is holding handles, then we need to notify the GC of them
|
||||
int typeId = objType->GetSubTypeId();
|
||||
if (typeId & asTYPEID_MASK_OBJECT)
|
||||
{
|
||||
void** d = (void**)buffer->data;
|
||||
for (asUINT n = 0; n < buffer->numElements; ++n)
|
||||
{
|
||||
if (d[n])
|
||||
engine->GCEnumCallback(d[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//void CScriptArray::EnumReferences(asIScriptEngine* engine)
|
||||
//{
|
||||
// // If the array is holding handles, then we need to notify the GC of them
|
||||
// int typeId = objType->GetSubTypeId();
|
||||
// if (typeId & asTYPEID_MASK_OBJECT)
|
||||
// {
|
||||
// void** d = (void**)buffer->data;
|
||||
// for (asUINT n = 0; n < buffer->numElements; ++n)
|
||||
// {
|
||||
// if (d[n])
|
||||
// engine->GCEnumCallback(d[n]);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptArray::ReleaseAllHandles(asIScriptEngine* engine)
|
||||
{
|
||||
// Resizing to zero will release everything
|
||||
Resize(0);
|
||||
}
|
||||
//void CScriptArray::ReleaseAllHandles(asIScriptEngine* engine)
|
||||
//{
|
||||
// // Resizing to zero will release everything
|
||||
// Resize(0);
|
||||
//}
|
||||
|
||||
void CScriptArray::AddRef() const
|
||||
{
|
||||
@ -557,27 +557,27 @@ void CScriptArray::Release() const
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
int CScriptArray::GetRefCount()
|
||||
{
|
||||
return refCount;
|
||||
}
|
||||
//int CScriptArray::GetRefCount()
|
||||
//{
|
||||
// return refCount;
|
||||
//}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptArray::SetFlag()
|
||||
{
|
||||
gcFlag = true;
|
||||
}
|
||||
//void CScriptArray::SetFlag()
|
||||
//{
|
||||
// gcFlag = true;
|
||||
//}
|
||||
|
||||
// GC behaviour
|
||||
bool CScriptArray::GetFlag()
|
||||
{
|
||||
return gcFlag;
|
||||
}
|
||||
//bool CScriptArray::GetFlag()
|
||||
//{
|
||||
// return gcFlag;
|
||||
//}
|
||||
|
||||
// Registers the template array type
|
||||
void registerArray(asIScriptEngine* engine)
|
||||
{
|
||||
engine->RegisterObjectType("array<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE);
|
||||
engine->RegisterObjectType("array<class T>", 0, asOBJ_REF | asOBJ_TEMPLATE);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in)", asFUNCTION(ScriptArrayTemplateCallback), asCALL_CDECL);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int& in)", asFUNCTIONPR(ScriptArrayFactory, (asIObjectType*), CScriptArray*), asCALL_CDECL);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int& in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL);
|
||||
@ -594,10 +594,10 @@ void registerArray(asIScriptEngine* engine)
|
||||
engine->RegisterObjectMethod("array<T>", "void removeLast()", asMETHOD(CScriptArray, RemoveLast), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("array<T>", "uint length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL);
|
||||
engine->RegisterObjectMethod("array<T>", "void resize(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptArray, GetRefCount), asCALL_THISCALL);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptArray, SetFlag), asCALL_THISCALL);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptArray, GetFlag), asCALL_THISCALL);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ENUMREFS, "void f(int& in)", asMETHOD(CScriptArray, EnumReferences), asCALL_THISCALL);
|
||||
engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASEREFS, "void f(int& in)", asMETHOD(CScriptArray, ReleaseAllHandles), asCALL_THISCALL);
|
||||
//engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptArray, GetRefCount), asCALL_THISCALL);
|
||||
//engine->RegisterObjectBehaviour("array<T>", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptArray, SetFlag), asCALL_THISCALL);
|
||||
//engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptArray, GetFlag), asCALL_THISCALL);
|
||||
//engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ENUMREFS, "void f(int& in)", asMETHOD(CScriptArray, EnumReferences), asCALL_THISCALL);
|
||||
//engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASEREFS, "void f(int& in)", asMETHOD(CScriptArray, ReleaseAllHandles), asCALL_THISCALL);
|
||||
engine->RegisterDefaultArrayType("array<T>");
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "RegisterArray.h"
|
||||
#include "RegisterStdString.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "ScriptFile.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include <angelscript.h>
|
||||
@ -128,14 +129,14 @@ void ScriptEngine::garbageCollect(bool fullCycle)
|
||||
{
|
||||
PROFILE(Script_GarbageCollect);
|
||||
|
||||
// Run either a number of iterations, or a full cycle if requested
|
||||
if (fullCycle)
|
||||
mAngelScriptEngine->GarbageCollect(asGC_FULL_CYCLE);
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0; i < GARBAGE_COLLECT_ITERATIONS; ++i)
|
||||
mAngelScriptEngine->GarbageCollect(asGC_ONE_STEP);
|
||||
}
|
||||
// Unprepare contexts up to the highest used
|
||||
mImmediateContext->Unprepare();
|
||||
unsigned highest = getHighestScriptNestingLevel();
|
||||
for (unsigned i = 0; i < highest; ++i)
|
||||
mScriptFileContexts[i]->Unprepare();
|
||||
|
||||
// Then actually garbage collect
|
||||
mAngelScriptEngine->GarbageCollect(fullCycle ? asGC_FULL_CYCLE : asGC_ONE_STEP);
|
||||
}
|
||||
|
||||
void ScriptEngine::setLogMode(ScriptLogMode mode)
|
||||
|
@ -40,9 +40,6 @@ enum ScriptLogMode
|
||||
//! Maximum function/method nesting level
|
||||
static const unsigned MAX_SCRIPT_NESTING_LEVEL = 32;
|
||||
|
||||
//! Amount of garbage collection iterations when not doing a full cycle
|
||||
static const unsigned GARBAGE_COLLECT_ITERATIONS = 5;
|
||||
|
||||
//! Utilizes the AngelScript library for executing scripts
|
||||
class ScriptEngine : public RefCounted
|
||||
{
|
||||
|
@ -37,7 +37,8 @@
|
||||
|
||||
#include "DebugNew.h"
|
||||
|
||||
static int scriptNestingLevel = 0;
|
||||
static unsigned scriptNestingLevel = 0;
|
||||
static unsigned highestScriptNestingLevel = 0;
|
||||
ScriptFile* lastScriptFile = 0;
|
||||
|
||||
ScriptFile::ScriptFile(ScriptEngine* scriptEngine, const std::string& name) :
|
||||
@ -59,13 +60,13 @@ ScriptFile::~ScriptFile()
|
||||
for (std::vector<ScriptInstance*>::iterator i = instances.begin(); i != instances.end(); ++i)
|
||||
(*i)->releaseObject();
|
||||
|
||||
// Perform a full garbage collection cycle, also clean up contexts which might still refer to the module's functions
|
||||
mScriptEngine->garbageCollect(true);
|
||||
|
||||
// Remove the module
|
||||
asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
|
||||
engine->DiscardModule(getName().c_str());
|
||||
mScriptModule = 0;
|
||||
|
||||
// Perform a full garbage collection cycle
|
||||
mScriptEngine->garbageCollect(true);
|
||||
}
|
||||
if (lastScriptFile == this)
|
||||
lastScriptFile = 0;
|
||||
@ -103,12 +104,12 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
|
||||
mScriptEngine->setLogMode(LOGMODE_RETAINED);
|
||||
mScriptEngine->clearLogMessages();
|
||||
int result = mScriptModule->Build();
|
||||
std::string errors = mScriptEngine->getLogMessages();
|
||||
mScriptEngine->setLogMode(LOGMODE_IMMEDIATE);
|
||||
if (result < 0)
|
||||
{
|
||||
std::string errors = mScriptEngine->getLogMessages();
|
||||
EXCEPTION("Failed to compile script module " + getName() + ":\n" + errors);
|
||||
}
|
||||
if (!errors.empty())
|
||||
LOGWARNING(errors);
|
||||
|
||||
LOGINFO("Compiled script module " + getName());
|
||||
mCompiled = true;
|
||||
@ -170,6 +171,8 @@ bool ScriptFile::execute(asIScriptFunction* function, const std::vector<Variant>
|
||||
setParameters(context, function, parameters);
|
||||
|
||||
++scriptNestingLevel;
|
||||
if (scriptNestingLevel > highestScriptNestingLevel)
|
||||
highestScriptNestingLevel = scriptNestingLevel;
|
||||
bool success = context->Execute() >= 0;
|
||||
--scriptNestingLevel;
|
||||
|
||||
@ -212,6 +215,8 @@ bool ScriptFile::execute(asIScriptObject* object, asIScriptFunction* method, con
|
||||
setParameters(context, method, parameters);
|
||||
|
||||
++scriptNestingLevel;
|
||||
if (scriptNestingLevel > highestScriptNestingLevel)
|
||||
highestScriptNestingLevel = scriptNestingLevel;
|
||||
bool success = context->Execute() >= 0;
|
||||
--scriptNestingLevel;
|
||||
|
||||
@ -530,3 +535,10 @@ unsigned getScriptNestingLevel()
|
||||
{
|
||||
return scriptNestingLevel;
|
||||
}
|
||||
|
||||
unsigned getHighestScriptNestingLevel()
|
||||
{
|
||||
unsigned ret = highestScriptNestingLevel;
|
||||
highestScriptNestingLevel = 0;
|
||||
return ret;
|
||||
}
|
||||
|
@ -116,5 +116,7 @@ private:
|
||||
ScriptFile* getLastScriptFile();
|
||||
//! Get current script execution nesting level
|
||||
unsigned getScriptNestingLevel();
|
||||
//! Return highest script execution nesting level last frame, and clear
|
||||
unsigned getHighestScriptNestingLevel();
|
||||
|
||||
#endif // SCRIPT_SCRIPTFILE_H
|
||||
|
@ -217,7 +217,7 @@ void AIController::control(Ninja* ninja, float time)
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not, walk idly
|
||||
// If no target, walk idly
|
||||
ninja->mControls.set(CTRL_ALL, false);
|
||||
ninja->mControls.set(CTRL_UP, true);
|
||||
ninja->mDirChangeTime -= time;
|
||||
|
@ -918,7 +918,7 @@ void Game::spawnObjects(float timeStep)
|
||||
float maxOffset = GameConfig::getReal("Game/EnemySpawnOffset");
|
||||
float offset = random(maxOffset * 2.0f) - maxOffset;
|
||||
// Random north/east/south/west direction
|
||||
int dir = random(4);
|
||||
int dir = rand() & 3;
|
||||
dir *= 90;
|
||||
Quaternion q((float)dir, Vector3::sUp);
|
||||
Vector3 position(q * (GameConfig::getVector3("Game/EnemySpawnPosition") + Vector3(offset, 0, 0)));
|
||||
|
@ -38,10 +38,6 @@
|
||||
|
||||
Application::Application()
|
||||
{
|
||||
std::string userDir = getUserDocumentsDirectory();
|
||||
std::string applicationDir = userDir + "Urho3D";
|
||||
|
||||
createDirectory(applicationDir);
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
@ -56,20 +52,11 @@ Application::~Application()
|
||||
|
||||
void Application::run()
|
||||
{
|
||||
init();
|
||||
// Create application directory
|
||||
std::string applicationDir = getUserDocumentsDirectory() + "Urho3D";
|
||||
createDirectory(applicationDir);
|
||||
|
||||
while (!mEngine->isExiting())
|
||||
{
|
||||
if (!mScriptFile->execute("void runFrame()"))
|
||||
EXCEPTION("Failed to execute the runFrame() function");
|
||||
|
||||
// Script garbage collection
|
||||
mEngine->getScriptEngine()->garbageCollect(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::init()
|
||||
{
|
||||
// Parse script file name from the command line
|
||||
const std::vector<std::string>& arguments = getArguments();
|
||||
std::string scriptFileName;
|
||||
if ((arguments.size()) && (arguments[0][0] != '-'))
|
||||
@ -78,12 +65,11 @@ void Application::init()
|
||||
EXCEPTION("Usage: Urho3D <scriptfile> [options]\n\n"
|
||||
"The script file should implement the functions void start() and void runFrame(). See the readme for options.\n");
|
||||
|
||||
// Instantiate the engine
|
||||
mEngine = new Engine();
|
||||
|
||||
// Add resources
|
||||
mCache = mEngine->getResourceCache();
|
||||
if (fileExists("CoreData.pak"))
|
||||
mCache->addPackageFile(new PackageFile("CoreData.pak"));
|
||||
if (fileExists("Data.pak"))
|
||||
mCache->addPackageFile(new PackageFile("Data.pak"));
|
||||
else
|
||||
@ -91,6 +77,7 @@ void Application::init()
|
||||
|
||||
mCache->addResourcePath(getSystemFontDirectory());
|
||||
|
||||
// Initialize engine & scripting
|
||||
mEngine->init(arguments);
|
||||
mEngine->createScriptEngine();
|
||||
|
||||
@ -100,4 +87,11 @@ void Application::init()
|
||||
throw Exception(getLog()->getLastMessage(), false);
|
||||
if (!mScriptFile->execute("void start()"))
|
||||
EXCEPTION("Failed to execute the start() function");
|
||||
|
||||
// Run until exited
|
||||
while (!mEngine->isExiting())
|
||||
{
|
||||
if (!mScriptFile->execute("void runFrame()"))
|
||||
EXCEPTION("Failed to execute the runFrame() function");
|
||||
}
|
||||
}
|
||||
|
@ -31,19 +31,24 @@ class Engine;
|
||||
class ResourceCache;
|
||||
class ScriptFile;
|
||||
|
||||
//! Urho3D Shell application
|
||||
class Application
|
||||
{
|
||||
public:
|
||||
//! Construct
|
||||
Application();
|
||||
//! Destruct
|
||||
~Application();
|
||||
|
||||
//! Run the script-based initialization & main loop
|
||||
void run();
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
//! Engine
|
||||
SharedPtr<Engine> mEngine;
|
||||
//! Resource cache
|
||||
SharedPtr<ResourceCache> mCache;
|
||||
//! Script file
|
||||
SharedPtr<ScriptFile> mScriptFile;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user