Sample2D.as 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. // Convenient functions for Urho2D samples:
  2. // - Generate collision shapes from a tmx file objects
  3. // - Create Spriter Imp character
  4. // - Load Mover script object class from file
  5. // - Create enemies, coins and platforms to tile map placeholders
  6. // - Handle camera zoom using PageUp, PageDown and MouseWheel
  7. // - Create UI interface
  8. // - Create a particle emitter attached to a given node
  9. // - Play a non-looping sound effect
  10. // - Load/Save the scene
  11. // - Set global variables
  12. // - Set XML patch instructions for screen joystick
  13. #include "Scripts/Utilities/2D/Mover.as"
  14. float CAMERA_MIN_DIST = 0.1f;
  15. float CAMERA_MAX_DIST = 6.0f;
  16. const float MOVE_SPEED = 23.0f; // Movement speed as world units per second
  17. const float MOVE_SPEED_X = 1.5f; // Movement speed as world units per second
  18. float MOVE_SPEED_SCALE = 1.0f; // Scaling factor based on tiles' aspect ratio
  19. const int LIFES = 3;
  20. float zoom = 2.0f; // Speed is scaled according to zoom
  21. String demoFilename = "";
  22. Node@ character2DNode;
  23. void CreateCollisionShapesFromTMXObjects(Node@ tileMapNode, TileMapLayer2D@ tileMapLayer, const TileMapInfo2D@ info)
  24. {
  25. // Create rigid body to the root node
  26. RigidBody2D@ body = tileMapNode.CreateComponent("RigidBody2D");
  27. body.bodyType = BT_STATIC;
  28. // Generate physics collision shapes and rigid bodies from the tmx file's objects located in "Physics" layer
  29. for (uint i = 0; i < tileMapLayer.numObjects; ++i)
  30. {
  31. TileMapObject2D@ tileMapObject = tileMapLayer.GetObject(i); // Get physics objects
  32. // Create collision shape from tmx object
  33. switch (tileMapObject.objectType)
  34. {
  35. case OT_RECTANGLE:
  36. CreateRectangleShape(tileMapNode, tileMapObject, tileMapObject.size, info);
  37. continue;
  38. case OT_ELLIPSE:
  39. CreateCircleShape(tileMapNode, tileMapObject, tileMapObject.size.x / 2, info); // Ellipse is built as a Circle shape as it doesn't exist in Box2D
  40. continue;
  41. case OT_POLYGON:
  42. CreatePolygonShape(tileMapNode, tileMapObject);
  43. continue;
  44. case OT_POLYLINE:
  45. CreatePolyLineShape(tileMapNode, tileMapObject);
  46. continue;
  47. default:
  48. continue;
  49. }
  50. }
  51. }
  52. CollisionBox2D@ CreateRectangleShape(Node@ node, TileMapObject2D@ object, Vector2 size, const TileMapInfo2D@ info)
  53. {
  54. CollisionBox2D@ shape = node.CreateComponent("CollisionBox2D");
  55. shape.size = size;
  56. if (info.orientation == O_ORTHOGONAL)
  57. shape.center = object.position + size / 2;
  58. else
  59. {
  60. shape.center = object.position + Vector2(info.tileWidth / 2, 0.0f);
  61. shape.angle = 45.0f; // If our tile map is isometric then shape is losange
  62. }
  63. shape.friction = 0.8f;
  64. if (object.HasProperty("Friction"))
  65. shape.friction = object.GetProperty("Friction").ToFloat();
  66. return shape;
  67. }
  68. CollisionCircle2D@ CreateCircleShape(Node@ node, TileMapObject2D@ object, float radius, const TileMapInfo2D@ info)
  69. {
  70. CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D");
  71. Vector2 size = object.size;
  72. shape.radius = radius;
  73. if (info.orientation == O_ORTHOGONAL)
  74. shape.center = object.position + size / 2;
  75. else
  76. {
  77. shape.center = object.position + Vector2(info.tileWidth / 2, 0.0f);
  78. }
  79. shape.friction = 0.8f;
  80. if (object.HasProperty("Friction"))
  81. shape.friction = object.GetProperty("Friction").ToFloat();
  82. return shape;
  83. }
  84. CollisionPolygon2D@ CreatePolygonShape(Node@ node, const TileMapObject2D@ object)
  85. {
  86. CollisionPolygon2D@ shape = node.CreateComponent("CollisionPolygon2D");
  87. uint numVertices = object.numPoints;
  88. shape.vertexCount = numVertices;
  89. for (uint i = 0; i < numVertices; ++i)
  90. shape.SetVertex(i, object.GetPoint(i));
  91. shape.friction = 0.8f;
  92. if (object.HasProperty("Friction"))
  93. shape.friction = object.GetProperty("Friction").ToFloat();
  94. return shape;
  95. }
  96. CollisionChain2D@ CreatePolyLineShape(Node@ node, TileMapObject2D@ object)
  97. {
  98. CollisionChain2D@ shape = node.CreateComponent("CollisionChain2D");
  99. uint numVertices = object.numPoints;
  100. shape.vertexCount = numVertices;
  101. for (uint i = 0; i < numVertices; ++i)
  102. shape.SetVertex(i, object.GetPoint(i));
  103. shape.friction = 0.8f;
  104. if (object.HasProperty("Friction"))
  105. shape.friction = object.GetProperty("Friction").ToFloat();
  106. return shape;
  107. }
  108. void CreateCharacter(const TileMapInfo2D@ info, bool createObject, float friction, Vector3 position, float scale)
  109. {
  110. character2DNode = scene_.CreateChild("Imp");
  111. character2DNode.position = position;
  112. character2DNode.SetScale(scale);
  113. AnimatedSprite2D@ animatedSprite = character2DNode.CreateComponent("AnimatedSprite2D");
  114. AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/imp/imp.scml");
  115. if (spriterAnimationSet is null)
  116. return;
  117. animatedSprite.animationSet = spriterAnimationSet;
  118. animatedSprite.SetAnimation("idle"); // Get scml file and Play "idle" anim
  119. animatedSprite.layer = 3; // Put character over tile map (which is on layer 0) and over Orcs (which are on layer 2)
  120. RigidBody2D@ characterBody = character2DNode.CreateComponent("RigidBody2D");
  121. characterBody.bodyType = BT_DYNAMIC;
  122. characterBody.allowSleep = false;
  123. CollisionCircle2D@ shape = character2DNode.CreateComponent("CollisionCircle2D");
  124. shape.radius = 1.1f; // Set shape size
  125. shape.friction = friction; // Set friction
  126. shape.restitution = 0.1f; // Bounce
  127. if (createObject)
  128. character2DNode.CreateScriptObject(scriptFile, "Character2D"); // Create a ScriptObject to handle character behavior
  129. // Scale character's speed on the Y axis according to tiles' aspect ratio (for isometric only)
  130. MOVE_SPEED_SCALE = info.tileHeight / info.tileWidth;
  131. }
  132. Node@ CreateTrigger()
  133. {
  134. Node@ node = scene_.CreateChild(); // Clones will be renamed according to object type
  135. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  136. body.bodyType = BT_STATIC;
  137. CollisionBox2D@ shape = node.CreateComponent("CollisionBox2D"); // Create box shape
  138. shape.trigger = true;
  139. return node;
  140. }
  141. Node@ CreateEnemy()
  142. {
  143. Node@ node = scene_.CreateChild("Enemy");
  144. StaticSprite2D@ staticSprite = node.CreateComponent("StaticSprite2D");
  145. staticSprite.sprite = cache.GetResource("Sprite2D", "Urho2D/Aster.png");
  146. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  147. body.bodyType = BT_STATIC;
  148. CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
  149. shape.radius = 0.25f; // Set radius
  150. return node;
  151. }
  152. Node@ CreateOrc()
  153. {
  154. Node@ node = scene_.CreateChild("Orc");
  155. node.scale = character2DNode.scale; // Use same scale as player character
  156. AnimatedSprite2D@ animatedSprite = node.CreateComponent("AnimatedSprite2D");
  157. AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/Orc/Orc.scml");
  158. if (spriterAnimationSet is null)
  159. return null;
  160. animatedSprite.animationSet = spriterAnimationSet;
  161. animatedSprite.SetAnimation("run"); // Get scml file and Play "run" anim
  162. animatedSprite.layer = 2; // Make orc always visible
  163. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  164. CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
  165. shape.radius = 1.3f; // Set shape size
  166. shape.trigger = true;
  167. return node;
  168. }
  169. Node@ CreateCoin()
  170. {
  171. Node@ node = scene_.CreateChild("Coin");
  172. node.SetScale(0.5);
  173. AnimatedSprite2D@ animatedSprite = node.CreateComponent("AnimatedSprite2D");
  174. animatedSprite.layer = 4;
  175. AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/GoldIcon.scml");
  176. if (spriterAnimationSet is null)
  177. return null;
  178. animatedSprite.animationSet = spriterAnimationSet;
  179. animatedSprite.SetAnimation("idle"); // Get scml file and Play "idle" anim
  180. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  181. body.bodyType = BT_STATIC;
  182. CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
  183. shape.radius = 0.32f; // Set radius
  184. shape.trigger = true;
  185. return node;
  186. }
  187. Node@ CreateMovingPlatform()
  188. {
  189. Node@ node = scene_.CreateChild("MovingPlatform");
  190. node.scale = Vector3(3.0f, 1.0f, 0.0f);
  191. StaticSprite2D@ staticSprite = node.CreateComponent("StaticSprite2D");
  192. staticSprite.sprite = cache.GetResource("Sprite2D", "Urho2D/Box.png");
  193. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  194. body.bodyType = BT_STATIC;
  195. CollisionBox2D@ shape = node.CreateComponent("CollisionBox2D"); // Create box shape
  196. shape.size = Vector2(0.32f, 0.32f); // Set box size
  197. shape.friction = 0.8f; // Set friction
  198. return node;
  199. }
  200. void PopulateMovingEntities(TileMapLayer2D@ movingEntitiesLayer)
  201. {
  202. // Create enemy, Orc and moving platform nodes (will be cloned at each placeholder)
  203. Node@ enemyNode = CreateEnemy();
  204. Node@ orcNode = CreateOrc();
  205. Node@ platformNode = CreateMovingPlatform();
  206. // Instantiate enemies and moving platforms at each placeholder (placeholders are Poly Line objects defining a path from points)
  207. for (uint i=0; i < movingEntitiesLayer.numObjects; ++i)
  208. {
  209. // Get placeholder object
  210. TileMapObject2D@ movingObject = movingEntitiesLayer.GetObject(i); // Get placeholder object
  211. if (movingObject.objectType == OT_POLYLINE)
  212. {
  213. // Clone the moving entity node and position it at placeholder point
  214. Node@ movingClone;
  215. Vector2 offset = Vector2(0.0f, 0.0f);
  216. if (movingObject.type == "Enemy")
  217. {
  218. movingClone = enemyNode.Clone();
  219. offset = Vector2(0.0f, -0.32f);
  220. }
  221. else if (movingObject.type == "Orc")
  222. movingClone = orcNode.Clone();
  223. else if (movingObject.type == "MovingPlatform")
  224. movingClone = platformNode.Clone();
  225. else
  226. continue;
  227. movingClone.position2D = movingObject.GetPoint(0) + offset;
  228. // Create script object that handles entity translation along its path (load from file included)
  229. Mover@ mover = cast<Mover>(movingClone.CreateScriptObject(scriptFile, "Mover"));
  230. // Set path from points
  231. mover.path = CreatePathFromPoints(movingObject, offset);
  232. // Override default speed
  233. if (movingObject.HasProperty("Speed"))
  234. mover.speed = movingObject.GetProperty("Speed").ToFloat();
  235. }
  236. }
  237. // Remove nodes used for cloning purpose
  238. enemyNode.Remove();
  239. orcNode.Remove();
  240. platformNode.Remove();
  241. }
  242. void PopulateCoins(TileMapLayer2D@ coinsLayer)
  243. {
  244. // Create coin (will be cloned at each placeholder)
  245. Node@ coinNode = CreateCoin();
  246. // Instantiate coins to pick at each placeholder
  247. for (uint i=0; i < coinsLayer.numObjects; ++i)
  248. {
  249. TileMapObject2D@ coinObject = coinsLayer.GetObject(i); // Get placeholder object
  250. Node@ coinClone = coinNode.Clone();
  251. coinClone.position2D = coinObject.position + coinObject.size / 2 + Vector2(0.0f, 0.16f);
  252. }
  253. // Init coins counters
  254. Character2D@ character = cast<Character2D>(character2DNode.scriptObject);
  255. character.remainingCoins = coinsLayer.numObjects;
  256. character.maxCoins = coinsLayer.numObjects;
  257. // Remove node used for cloning purpose
  258. coinNode.Remove();
  259. }
  260. void PopulateTriggers(TileMapLayer2D@ triggersLayer)
  261. {
  262. // Create trigger node (will be cloned at each placeholder)
  263. Node@ triggerNode = CreateTrigger();
  264. // Instantiate triggers at each placeholder (Rectangle objects)
  265. for (uint i=0; i < triggersLayer.numObjects; ++i)
  266. {
  267. TileMapObject2D@ triggerObject = triggersLayer.GetObject(i); // Get placeholder object
  268. if (triggerObject.objectType == OT_RECTANGLE)
  269. {
  270. Node@ triggerClone = triggerNode.Clone();
  271. triggerClone.name = triggerObject.type;
  272. CollisionBox2D@ shape = triggerClone.GetComponent("CollisionBox2D");
  273. shape.size = triggerObject.size;
  274. triggerClone.position2D = triggerObject.position + triggerObject.size / 2;
  275. }
  276. }
  277. }
  278. void Zoom(Camera@ camera)
  279. {
  280. if (input.mouseMoveWheel != 0)
  281. camera.zoom = Clamp(camera.zoom + input.mouseMoveWheel * 0.1, CAMERA_MIN_DIST, CAMERA_MAX_DIST);
  282. if (input.keyDown[KEY_PAGEUP])
  283. {
  284. zoom = Clamp(camera.zoom * 1.01f, CAMERA_MIN_DIST, CAMERA_MAX_DIST);
  285. camera.zoom = zoom;
  286. }
  287. if (input.keyDown[KEY_PAGEDOWN])
  288. {
  289. zoom = Clamp(camera.zoom * 0.99f, CAMERA_MIN_DIST, CAMERA_MAX_DIST);
  290. camera.zoom = zoom;
  291. }
  292. }
  293. Vector2[] CreatePathFromPoints(TileMapObject2D@ object, Vector2 offset)
  294. {
  295. Array<Vector2> path;
  296. for (uint i=0; i < object.numPoints; ++i)
  297. path.Push(object.GetPoint(i) + offset);
  298. return path;
  299. }
  300. void CreateUIContent(String demoTitle)
  301. {
  302. // Set the default UI style and font
  303. ui.root.defaultStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
  304. Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
  305. // We create in-game UIs (coins and lifes) first so that they are hidden by the fullscreen UI (we could also temporary hide them using SetVisible)
  306. // Create the UI for displaying the remaining coins
  307. BorderImage@ coinsUI = ui.root.CreateChild("BorderImage", "Coins");
  308. coinsUI.texture = cache.GetResource("Texture2D", "Urho2D/GoldIcon.png");
  309. coinsUI.SetSize(50, 50);
  310. coinsUI.imageRect = IntRect(0, 64, 60, 128);
  311. coinsUI.SetAlignment(HA_LEFT, VA_TOP);
  312. coinsUI.SetPosition(5, 5);
  313. Text@ coinsText = coinsUI.CreateChild("Text", "CoinsText");
  314. coinsText.SetAlignment(HA_CENTER, VA_CENTER);
  315. coinsText.SetFont(font, 24);
  316. coinsText.textEffect = TE_SHADOW;
  317. coinsText.text = cast<Character2D>(character2DNode.scriptObject).remainingCoins;
  318. // Create the UI for displaying the remaining lifes
  319. BorderImage@ lifeUI = ui.root.CreateChild("BorderImage", "Life");
  320. lifeUI.texture = cache.GetResource("Texture2D", "Urho2D/imp/imp_all.png");
  321. lifeUI.SetSize(70, 80);
  322. lifeUI.SetAlignment(HA_RIGHT, VA_TOP);
  323. lifeUI.SetPosition(-5, 5);
  324. Text@ lifeText = lifeUI.CreateChild("Text", "LifeText");
  325. lifeText.SetAlignment(HA_CENTER, VA_CENTER);
  326. lifeText.SetFont(font, 24);
  327. lifeText.textEffect = TE_SHADOW;
  328. lifeText.text = LIFES;
  329. // Create the fullscreen UI for start/end
  330. Window@ fullUI = ui.root.CreateChild("Window", "FullUI");
  331. fullUI.SetStyleAuto();
  332. fullUI.SetSize(ui.root.width, ui.root.height);
  333. fullUI.enabled = false; // Do not react to input, only the 'Exit' and 'Play' buttons will
  334. // Create the title
  335. BorderImage@ title = fullUI.CreateChild("BorderImage", "Title");
  336. title.SetMinSize(fullUI.width, 50);
  337. title.texture = cache.GetResource("Texture2D", "Textures/HeightMap.png");
  338. title.SetFullImageRect();
  339. title.SetAlignment(HA_CENTER, VA_TOP);
  340. Text@ titleText = title.CreateChild("Text", "TitleText");
  341. titleText.SetAlignment(HA_CENTER, VA_CENTER);
  342. titleText.SetFont(font, 24);
  343. titleText.text = demoTitle;
  344. // Create the image
  345. BorderImage@ spriteUI = fullUI.CreateChild("BorderImage", "Sprite");
  346. spriteUI.texture = cache.GetResource("Texture2D", "Urho2D/imp/imp_all.png");
  347. spriteUI.SetSize(238, 271);
  348. spriteUI.SetAlignment(HA_CENTER, VA_CENTER);
  349. spriteUI.SetPosition(0, - ui.root.height / 4);
  350. // Create the 'EXIT' button
  351. Button@ exitButton = ui.root.CreateChild("Button", "ExitButton");
  352. exitButton.SetStyleAuto();
  353. exitButton.focusMode = FM_RESETFOCUS;
  354. exitButton.SetSize(100, 50);
  355. exitButton.SetAlignment(HA_CENTER, VA_CENTER);
  356. exitButton.SetPosition(-100, 0);
  357. Text@ exitText = exitButton.CreateChild("Text", "ExitText");
  358. exitText.SetAlignment(HA_CENTER, VA_CENTER);
  359. exitText.SetFont(font, 24);
  360. exitText.text = "EXIT";
  361. SubscribeToEvent(exitButton, "Released", "HandleExitButton");
  362. // Create the 'PLAY' button
  363. Button@ playButton = ui.root.CreateChild("Button", "PlayButton");
  364. playButton.SetStyleAuto();
  365. playButton.focusMode = FM_RESETFOCUS;
  366. playButton.SetSize(100, 50);
  367. playButton.SetAlignment(HA_CENTER, VA_CENTER);
  368. playButton.SetPosition(100, 0);
  369. Text@ playText = playButton.CreateChild("Text", "PlayText");
  370. playText.SetAlignment(HA_CENTER, VA_CENTER);
  371. playText.SetFont(font, 24);
  372. playText.text = "PLAY";
  373. SubscribeToEvent(playButton, "Released", "HandlePlayButton");
  374. // Create the instructions
  375. Text@ instructionText = ui.root.CreateChild("Text", "Instructions");
  376. instructionText.SetFont(font, 15);
  377. instructionText.textAlignment = HA_CENTER; // Center rows in relation to each other
  378. instructionText.text = "Use WASD keys or Arrows to move\nPageUp/PageDown/MouseWheel to zoom\nF5/F7 to save/reload scene\n'Z' to toggle debug geometry\nSpace to fight";
  379. instructionText.SetAlignment(HA_CENTER, VA_CENTER);
  380. instructionText.SetPosition(0, ui.root.height / 4);
  381. // Show mouse cursor
  382. input.mouseVisible = true;
  383. }
  384. void HandleExitButton()
  385. {
  386. engine.Exit();
  387. }
  388. void HandlePlayButton()
  389. {
  390. // Remove fullscreen UI and unfreeze the scene
  391. if (ui.root.GetChild("FullUI", true) !is null)
  392. {
  393. ui.root.GetChild("FullUI", true).Remove();
  394. scene_.updateEnabled = true;
  395. }
  396. else
  397. {
  398. // Reload scene
  399. ReloadScene(true);
  400. }
  401. // Hide Instructions and Play/Exit buttons
  402. Text@ instructionText = ui.root.GetChild("Instructions", true);
  403. instructionText.text = "";
  404. Button@ exitButton = ui.root.GetChild("ExitButton", true);
  405. exitButton.visible = false;
  406. Button@ playButton = ui.root.GetChild("PlayButton", true);
  407. playButton.visible = false;
  408. // Hide mouse cursor
  409. input.mouseVisible = false;
  410. }
  411. void SaveScene(bool initial)
  412. {
  413. String filename = demoFilename;
  414. if (!initial)
  415. filename = demoFilename + "InGame";
  416. File saveFile(fileSystem.programDir + "Data/Scenes/" + filename + ".xml", FILE_WRITE);
  417. scene_.SaveXML(saveFile);
  418. }
  419. void ReloadScene(bool reInit)
  420. {
  421. String filename = demoFilename;
  422. if (!reInit)
  423. filename = demoFilename + "InGame";
  424. File loadFile(fileSystem.programDir + "Data/Scenes/" + filename + ".xml", FILE_READ);
  425. scene_.LoadXML(loadFile);
  426. // After loading we have to reacquire the character2D scene node, as it has been recreated
  427. // Simply find by name as there's only one of them
  428. character2DNode = scene_.GetChild("Imp", true);
  429. if (character2DNode is null)
  430. return;
  431. // Set what value to use depending whether reload is requested from 'PLAY' button (reInit=true) or 'F7' key (reInit=false)
  432. Character2D@ character = cast<Character2D>(character2DNode.scriptObject);
  433. int lifes = character.remainingLifes;
  434. int coins = character.remainingCoins;
  435. if (reInit)
  436. {
  437. lifes = LIFES;
  438. coins = character.maxCoins;
  439. }
  440. // Update lifes UI and variable
  441. Text@ lifeText = ui.root.GetChild("LifeText", true);
  442. lifeText.text = lifes;
  443. character.remainingLifes = lifes;
  444. // Update coins UI and variable
  445. Text@ coinsText = ui.root.GetChild("CoinsText", true);
  446. coinsText.text = coins;
  447. character.remainingCoins = coins;
  448. }
  449. void SpawnEffect(Node@ node)
  450. {
  451. Node@ particleNode = node.CreateChild("Emitter");
  452. particleNode.SetScale(0.5 / node.scale.x);
  453. ParticleEmitter2D@ particleEmitter = particleNode.CreateComponent("ParticleEmitter2D");
  454. particleEmitter.effect = cache.GetResource("ParticleEffect2D", "Urho2D/sun.pex");
  455. particleEmitter.layer = 2;
  456. }
  457. void PlaySound(String soundName)
  458. {
  459. Node@ soundNode = scene_.CreateChild("Sound");
  460. SoundSource@ source = soundNode.CreateComponent("SoundSource");
  461. source.Play(cache.GetResource("Sound", "Sounds/" + soundName));
  462. }
  463. void CreateBackgroundSprite(const TileMapInfo2D@ info, float scale, String texture, bool animate)
  464. {
  465. Node@ node = scene_.CreateChild("Background");
  466. node.position = Vector3(info.mapWidth, info.mapHeight, 0) / 2;
  467. node.SetScale(scale);
  468. StaticSprite2D@ sprite = node.CreateComponent("StaticSprite2D");
  469. sprite.sprite = cache.GetResource("Sprite2D", texture);
  470. SetRandomSeed(time.systemTime); // Randomize from system clock
  471. sprite.color = Color(Random(0.0f, 1.0f), Random(0.0f, 1.0f), Random(0.0f, 1.0f), 1.0f);
  472. sprite.layer = -99;
  473. // Create rotation animation
  474. if (animate)
  475. {
  476. ValueAnimation@ animation = ValueAnimation();
  477. animation.SetKeyFrame(0, Variant(Quaternion(0.0f, 0.0f, 0.0f)));
  478. animation.SetKeyFrame(1, Variant(Quaternion(0.0f, 0.0f, 180.0f)));
  479. animation.SetKeyFrame(2, Variant(Quaternion(0.0f, 0.0f, 0.0f)));
  480. node.SetAttributeAnimation("Rotation", animation, WM_LOOP, 0.05f);
  481. }
  482. }
  483. // Create XML patch instructions for screen joystick layout specific to this sample app
  484. String patchInstructions =
  485. "<patch>" +
  486. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
  487. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Fight</replace>" +
  488. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
  489. " <element type=\"Text\">" +
  490. " <attribute name=\"Name\" value=\"KeyBinding\" />" +
  491. " <attribute name=\"Text\" value=\"SPACE\" />" +
  492. " </element>" +
  493. " </add>" +
  494. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
  495. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Jump</replace>" +
  496. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
  497. " <element type=\"Text\">" +
  498. " <attribute name=\"Name\" value=\"KeyBinding\" />" +
  499. " <attribute name=\"Text\" value=\"UP\" />" +
  500. " </element>" +
  501. " </add>" +
  502. "</patch>";