Sample2D.as 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. // Convenient functions for Urho2D and Physics2D 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. void CreatePolyLineShape(Node@ node, TileMapObject2D@ object)
  97. {
  98. /*
  99. CollisionChain2D@ shape = node.CreateComponent("CollisionChain2D");
  100. uint numVertices = object.numPoints;
  101. shape.vertexCount = numVertices;
  102. for (uint i = 0; i < numVertices; ++i)
  103. shape.SetVertex(i, object.GetPoint(i));
  104. shape.friction = 0.8f;
  105. if (object.HasProperty("Friction"))
  106. shape.friction = object.GetProperty("Friction").ToFloat();
  107. return shape;
  108. */
  109. // Latest Box2D supports only one sided chains with ghost vertices, use two sided edges instead.
  110. // But this can cause stuck at the edges ends https://box2d.org/posts/2020/06/ghost-collisions/
  111. int numVertices = object.numPoints;
  112. for (int i = 1; i < numVertices; ++i)
  113. {
  114. CollisionEdge2D@ shape = node.CreateComponent("CollisionEdge2D");
  115. shape.SetVertices(object.GetPoint(i - 1), object.GetPoint(i));
  116. shape.friction = 0.8f;
  117. if (object.HasProperty("Friction"))
  118. shape.friction = object.GetProperty("Friction").ToFloat();
  119. }
  120. }
  121. void CreateCharacter(const TileMapInfo2D@ info, bool createObject, float friction, Vector3 position, float scale)
  122. {
  123. character2DNode = scene_.CreateChild("Imp");
  124. character2DNode.position = position;
  125. character2DNode.SetScale(scale);
  126. AnimatedSprite2D@ animatedSprite = character2DNode.CreateComponent("AnimatedSprite2D");
  127. AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/imp/imp.scml");
  128. if (spriterAnimationSet is null)
  129. return;
  130. animatedSprite.animationSet = spriterAnimationSet;
  131. animatedSprite.SetAnimation("idle"); // Get scml file and Play "idle" anim
  132. animatedSprite.layer = 3; // Put character over tile map (which is on layer 0) and over Orcs (which are on layer 2)
  133. RigidBody2D@ characterBody = character2DNode.CreateComponent("RigidBody2D");
  134. characterBody.bodyType = BT_DYNAMIC;
  135. characterBody.allowSleep = false;
  136. characterBody.fixedRotation = true;
  137. CollisionCircle2D@ shape = character2DNode.CreateComponent("CollisionCircle2D");
  138. shape.radius = 1.1f; // Set shape size
  139. shape.friction = friction; // Set friction
  140. shape.restitution = 0.1f; // Bounce
  141. shape.density = 6.6f;
  142. if (createObject)
  143. character2DNode.CreateScriptObject(scriptFile, "Character2D"); // Create a ScriptObject to handle character behavior
  144. // Scale character's speed on the Y axis according to tiles' aspect ratio (for isometric only)
  145. MOVE_SPEED_SCALE = info.tileHeight / info.tileWidth;
  146. }
  147. Node@ CreateTrigger()
  148. {
  149. Node@ node = scene_.CreateChild(); // Clones will be renamed according to object type
  150. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  151. body.bodyType = BT_STATIC;
  152. CollisionBox2D@ shape = node.CreateComponent("CollisionBox2D"); // Create box shape
  153. shape.trigger = true;
  154. return node;
  155. }
  156. Node@ CreateEnemy()
  157. {
  158. Node@ node = scene_.CreateChild("Enemy");
  159. StaticSprite2D@ staticSprite = node.CreateComponent("StaticSprite2D");
  160. staticSprite.sprite = cache.GetResource("Sprite2D", "Urho2D/Aster.png");
  161. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  162. body.bodyType = BT_STATIC;
  163. CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
  164. shape.radius = 0.25f; // Set radius
  165. return node;
  166. }
  167. Node@ CreateOrc()
  168. {
  169. Node@ node = scene_.CreateChild("Orc");
  170. node.scale = character2DNode.scale; // Use same scale as player character
  171. AnimatedSprite2D@ animatedSprite = node.CreateComponent("AnimatedSprite2D");
  172. AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/Orc/Orc.scml");
  173. if (spriterAnimationSet is null)
  174. return null;
  175. animatedSprite.animationSet = spriterAnimationSet;
  176. animatedSprite.SetAnimation("run"); // Get scml file and Play "run" anim
  177. animatedSprite.layer = 2; // Make orc always visible
  178. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  179. CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
  180. shape.radius = 1.3f; // Set shape size
  181. shape.trigger = true;
  182. return node;
  183. }
  184. Node@ CreateCoin()
  185. {
  186. Node@ node = scene_.CreateChild("Coin");
  187. node.SetScale(0.5);
  188. AnimatedSprite2D@ animatedSprite = node.CreateComponent("AnimatedSprite2D");
  189. animatedSprite.layer = 4;
  190. AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/GoldIcon.scml");
  191. if (spriterAnimationSet is null)
  192. return null;
  193. animatedSprite.animationSet = spriterAnimationSet;
  194. animatedSprite.SetAnimation("idle"); // Get scml file and Play "idle" anim
  195. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  196. body.bodyType = BT_STATIC;
  197. CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
  198. shape.radius = 0.32f; // Set radius
  199. shape.trigger = true;
  200. return node;
  201. }
  202. Node@ CreateMovingPlatform()
  203. {
  204. Node@ node = scene_.CreateChild("MovingPlatform");
  205. node.scale = Vector3(3.0f, 1.0f, 0.0f);
  206. StaticSprite2D@ staticSprite = node.CreateComponent("StaticSprite2D");
  207. staticSprite.sprite = cache.GetResource("Sprite2D", "Urho2D/Box.png");
  208. RigidBody2D@ body = node.CreateComponent("RigidBody2D");
  209. body.bodyType = BT_STATIC;
  210. CollisionBox2D@ shape = node.CreateComponent("CollisionBox2D"); // Create box shape
  211. shape.size = Vector2(0.32f, 0.32f); // Set box size
  212. shape.friction = 0.8f; // Set friction
  213. return node;
  214. }
  215. void PopulateMovingEntities(TileMapLayer2D@ movingEntitiesLayer)
  216. {
  217. // Create enemy, Orc and moving platform nodes (will be cloned at each placeholder)
  218. Node@ enemyNode = CreateEnemy();
  219. Node@ orcNode = CreateOrc();
  220. Node@ platformNode = CreateMovingPlatform();
  221. // Instantiate enemies and moving platforms at each placeholder (placeholders are Poly Line objects defining a path from points)
  222. for (uint i=0; i < movingEntitiesLayer.numObjects; ++i)
  223. {
  224. // Get placeholder object
  225. TileMapObject2D@ movingObject = movingEntitiesLayer.GetObject(i); // Get placeholder object
  226. if (movingObject.objectType == OT_POLYLINE)
  227. {
  228. // Clone the moving entity node and position it at placeholder point
  229. Node@ movingClone;
  230. Vector2 offset = Vector2(0.0f, 0.0f);
  231. if (movingObject.type == "Enemy")
  232. {
  233. movingClone = enemyNode.Clone();
  234. offset = Vector2(0.0f, -0.32f);
  235. }
  236. else if (movingObject.type == "Orc")
  237. movingClone = orcNode.Clone();
  238. else if (movingObject.type == "MovingPlatform")
  239. movingClone = platformNode.Clone();
  240. else
  241. continue;
  242. movingClone.position2D = movingObject.GetPoint(0) + offset;
  243. // Create script object that handles entity translation along its path (load from file included)
  244. Mover@ mover = cast<Mover>(movingClone.CreateScriptObject(scriptFile, "Mover"));
  245. // Set path from points
  246. mover.path = CreatePathFromPoints(movingObject, offset);
  247. // Override default speed
  248. if (movingObject.HasProperty("Speed"))
  249. mover.speed = movingObject.GetProperty("Speed").ToFloat();
  250. }
  251. }
  252. // Remove nodes used for cloning purpose
  253. enemyNode.Remove();
  254. orcNode.Remove();
  255. platformNode.Remove();
  256. }
  257. void PopulateCoins(TileMapLayer2D@ coinsLayer)
  258. {
  259. // Create coin (will be cloned at each placeholder)
  260. Node@ coinNode = CreateCoin();
  261. // Instantiate coins to pick at each placeholder
  262. for (uint i=0; i < coinsLayer.numObjects; ++i)
  263. {
  264. TileMapObject2D@ coinObject = coinsLayer.GetObject(i); // Get placeholder object
  265. Node@ coinClone = coinNode.Clone();
  266. coinClone.position2D = coinObject.position + coinObject.size / 2 + Vector2(0.0f, 0.16f);
  267. }
  268. // Init coins counters
  269. Character2D@ character = cast<Character2D>(character2DNode.scriptObject);
  270. character.remainingCoins = coinsLayer.numObjects;
  271. character.maxCoins = coinsLayer.numObjects;
  272. // Remove node used for cloning purpose
  273. coinNode.Remove();
  274. }
  275. void PopulateTriggers(TileMapLayer2D@ triggersLayer)
  276. {
  277. // Create trigger node (will be cloned at each placeholder)
  278. Node@ triggerNode = CreateTrigger();
  279. // Instantiate triggers at each placeholder (Rectangle objects)
  280. for (uint i=0; i < triggersLayer.numObjects; ++i)
  281. {
  282. TileMapObject2D@ triggerObject = triggersLayer.GetObject(i); // Get placeholder object
  283. if (triggerObject.objectType == OT_RECTANGLE)
  284. {
  285. Node@ triggerClone = triggerNode.Clone();
  286. triggerClone.name = triggerObject.type;
  287. CollisionBox2D@ shape = triggerClone.GetComponent("CollisionBox2D");
  288. shape.size = triggerObject.size;
  289. triggerClone.position2D = triggerObject.position + triggerObject.size / 2;
  290. }
  291. }
  292. }
  293. void Zoom(Camera@ camera)
  294. {
  295. if (input.mouseMoveWheel != 0)
  296. camera.zoom = Clamp(camera.zoom + input.mouseMoveWheel * 0.1, CAMERA_MIN_DIST, CAMERA_MAX_DIST);
  297. if (input.keyDown[KEY_PAGEUP])
  298. {
  299. zoom = Clamp(camera.zoom * 1.01f, CAMERA_MIN_DIST, CAMERA_MAX_DIST);
  300. camera.zoom = zoom;
  301. }
  302. if (input.keyDown[KEY_PAGEDOWN])
  303. {
  304. zoom = Clamp(camera.zoom * 0.99f, CAMERA_MIN_DIST, CAMERA_MAX_DIST);
  305. camera.zoom = zoom;
  306. }
  307. }
  308. Vector2[] CreatePathFromPoints(TileMapObject2D@ object, Vector2 offset)
  309. {
  310. Array<Vector2> path;
  311. for (uint i=0; i < object.numPoints; ++i)
  312. path.Push(object.GetPoint(i) + offset);
  313. return path;
  314. }
  315. void CreateUIContent(String demoTitle)
  316. {
  317. // Set the default UI style and font
  318. ui.root.defaultStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
  319. Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
  320. // 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)
  321. // Create the UI for displaying the remaining coins
  322. BorderImage@ coinsUI = ui.root.CreateChild("BorderImage", "Coins");
  323. coinsUI.texture = cache.GetResource("Texture2D", "Urho2D/GoldIcon.png");
  324. coinsUI.SetSize(50, 50);
  325. coinsUI.imageRect = IntRect(0, 64, 60, 128);
  326. coinsUI.SetAlignment(HA_LEFT, VA_TOP);
  327. coinsUI.SetPosition(5, 5);
  328. Text@ coinsText = coinsUI.CreateChild("Text", "CoinsText");
  329. coinsText.SetAlignment(HA_CENTER, VA_CENTER);
  330. coinsText.SetFont(font, 24);
  331. coinsText.textEffect = TE_SHADOW;
  332. coinsText.text = cast<Character2D>(character2DNode.scriptObject).remainingCoins;
  333. // Create the UI for displaying the remaining lifes
  334. BorderImage@ lifeUI = ui.root.CreateChild("BorderImage", "Life");
  335. lifeUI.texture = cache.GetResource("Texture2D", "Urho2D/imp/imp_all.png");
  336. lifeUI.SetSize(70, 80);
  337. lifeUI.SetAlignment(HA_RIGHT, VA_TOP);
  338. lifeUI.SetPosition(-5, 5);
  339. Text@ lifeText = lifeUI.CreateChild("Text", "LifeText");
  340. lifeText.SetAlignment(HA_CENTER, VA_CENTER);
  341. lifeText.SetFont(font, 24);
  342. lifeText.textEffect = TE_SHADOW;
  343. lifeText.text = LIFES;
  344. // Create the fullscreen UI for start/end
  345. Window@ fullUI = ui.root.CreateChild("Window", "FullUI");
  346. fullUI.SetStyleAuto();
  347. fullUI.SetSize(ui.root.width, ui.root.height);
  348. fullUI.enabled = false; // Do not react to input, only the 'Exit' and 'Play' buttons will
  349. // Create the title
  350. BorderImage@ title = fullUI.CreateChild("BorderImage", "Title");
  351. title.SetMinSize(fullUI.width, 50);
  352. title.texture = cache.GetResource("Texture2D", "Textures/HeightMap.png");
  353. title.SetFullImageRect();
  354. title.SetAlignment(HA_CENTER, VA_TOP);
  355. Text@ titleText = title.CreateChild("Text", "TitleText");
  356. titleText.SetAlignment(HA_CENTER, VA_CENTER);
  357. titleText.SetFont(font, 24);
  358. titleText.text = demoTitle;
  359. // Create the image
  360. BorderImage@ spriteUI = fullUI.CreateChild("BorderImage", "Sprite");
  361. spriteUI.texture = cache.GetResource("Texture2D", "Urho2D/imp/imp_all.png");
  362. spriteUI.SetSize(238, 271);
  363. spriteUI.SetAlignment(HA_CENTER, VA_CENTER);
  364. spriteUI.SetPosition(0, - ui.root.height / 4);
  365. // Create the 'EXIT' button
  366. Button@ exitButton = ui.root.CreateChild("Button", "ExitButton");
  367. exitButton.SetStyleAuto();
  368. exitButton.focusMode = FM_RESETFOCUS;
  369. exitButton.SetSize(100, 50);
  370. exitButton.SetAlignment(HA_CENTER, VA_CENTER);
  371. exitButton.SetPosition(-100, 0);
  372. Text@ exitText = exitButton.CreateChild("Text", "ExitText");
  373. exitText.SetAlignment(HA_CENTER, VA_CENTER);
  374. exitText.SetFont(font, 24);
  375. exitText.text = "EXIT";
  376. SubscribeToEvent(exitButton, "Released", "HandleExitButton");
  377. // Create the 'PLAY' button
  378. Button@ playButton = ui.root.CreateChild("Button", "PlayButton");
  379. playButton.SetStyleAuto();
  380. playButton.focusMode = FM_RESETFOCUS;
  381. playButton.SetSize(100, 50);
  382. playButton.SetAlignment(HA_CENTER, VA_CENTER);
  383. playButton.SetPosition(100, 0);
  384. Text@ playText = playButton.CreateChild("Text", "PlayText");
  385. playText.SetAlignment(HA_CENTER, VA_CENTER);
  386. playText.SetFont(font, 24);
  387. playText.text = "PLAY";
  388. SubscribeToEvent(playButton, "Released", "HandlePlayButton");
  389. // Create the instructions
  390. Text@ instructionText = ui.root.CreateChild("Text", "Instructions");
  391. instructionText.SetFont(font, 15);
  392. instructionText.textAlignment = HA_CENTER; // Center rows in relation to each other
  393. 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";
  394. instructionText.SetAlignment(HA_CENTER, VA_CENTER);
  395. instructionText.SetPosition(0, ui.root.height / 4);
  396. // Show mouse cursor
  397. input.mouseVisible = true;
  398. }
  399. void HandleExitButton()
  400. {
  401. engine.Exit();
  402. }
  403. void HandlePlayButton()
  404. {
  405. // Remove fullscreen UI and unfreeze the scene
  406. if (ui.root.GetChild("FullUI", true) !is null)
  407. {
  408. ui.root.GetChild("FullUI", true).Remove();
  409. scene_.updateEnabled = true;
  410. }
  411. else
  412. {
  413. // Reload scene
  414. ReloadScene(true);
  415. }
  416. // Hide Instructions and Play/Exit buttons
  417. Text@ instructionText = ui.root.GetChild("Instructions", true);
  418. instructionText.text = "";
  419. Button@ exitButton = ui.root.GetChild("ExitButton", true);
  420. exitButton.visible = false;
  421. Button@ playButton = ui.root.GetChild("PlayButton", true);
  422. playButton.visible = false;
  423. // Hide mouse cursor
  424. input.mouseVisible = false;
  425. }
  426. void SaveScene(bool initial)
  427. {
  428. String filename = demoFilename;
  429. if (!initial)
  430. filename = demoFilename + "InGame";
  431. File saveFile(fileSystem.programDir + "Data/Scenes/" + filename + ".xml", FILE_WRITE);
  432. scene_.SaveXML(saveFile);
  433. }
  434. void ReloadScene(bool reInit)
  435. {
  436. String filename = demoFilename;
  437. if (!reInit)
  438. filename = demoFilename + "InGame";
  439. File loadFile(fileSystem.programDir + "Data/Scenes/" + filename + ".xml", FILE_READ);
  440. scene_.LoadXML(loadFile);
  441. // After loading we have to reacquire the character2D scene node, as it has been recreated
  442. // Simply find by name as there's only one of them
  443. character2DNode = scene_.GetChild("Imp", true);
  444. if (character2DNode is null)
  445. return;
  446. // Set what value to use depending whether reload is requested from 'PLAY' button (reInit=true) or 'F7' key (reInit=false)
  447. Character2D@ character = cast<Character2D>(character2DNode.scriptObject);
  448. int lifes = character.remainingLifes;
  449. int coins = character.remainingCoins;
  450. if (reInit)
  451. {
  452. lifes = LIFES;
  453. coins = character.maxCoins;
  454. }
  455. // Update lifes UI and variable
  456. Text@ lifeText = ui.root.GetChild("LifeText", true);
  457. lifeText.text = lifes;
  458. character.remainingLifes = lifes;
  459. // Update coins UI and variable
  460. Text@ coinsText = ui.root.GetChild("CoinsText", true);
  461. coinsText.text = coins;
  462. character.remainingCoins = coins;
  463. }
  464. void SpawnEffect(Node@ node)
  465. {
  466. Node@ particleNode = node.CreateChild("Emitter");
  467. particleNode.SetScale(0.5 / node.scale.x);
  468. ParticleEmitter2D@ particleEmitter = particleNode.CreateComponent("ParticleEmitter2D");
  469. particleEmitter.effect = cache.GetResource("ParticleEffect2D", "Urho2D/sun.pex");
  470. particleEmitter.layer = 2;
  471. }
  472. void PlaySound(String soundName)
  473. {
  474. Node@ soundNode = scene_.CreateChild("Sound");
  475. SoundSource@ source = soundNode.CreateComponent("SoundSource");
  476. source.Play(cache.GetResource("Sound", "Sounds/" + soundName));
  477. }
  478. void CreateBackgroundSprite(const TileMapInfo2D@ info, float scale, String texture, bool animate)
  479. {
  480. Node@ node = scene_.CreateChild("Background");
  481. node.position = Vector3(info.mapWidth, info.mapHeight, 0) / 2;
  482. node.SetScale(scale);
  483. StaticSprite2D@ sprite = node.CreateComponent("StaticSprite2D");
  484. sprite.sprite = cache.GetResource("Sprite2D", texture);
  485. SetRandomSeed(time.systemTime); // Randomize from system clock
  486. sprite.color = Color(Random(0.0f, 1.0f), Random(0.0f, 1.0f), Random(0.0f, 1.0f), 1.0f);
  487. sprite.layer = -99;
  488. // Create rotation animation
  489. if (animate)
  490. {
  491. ValueAnimation@ animation = ValueAnimation();
  492. animation.SetKeyFrame(0, Variant(Quaternion(0.0f, 0.0f, 0.0f)));
  493. animation.SetKeyFrame(1, Variant(Quaternion(0.0f, 0.0f, 180.0f)));
  494. animation.SetKeyFrame(2, Variant(Quaternion(0.0f, 0.0f, 0.0f)));
  495. node.SetAttributeAnimation("Rotation", animation, WM_LOOP, 0.05f);
  496. }
  497. }
  498. // Create XML patch instructions for screen joystick layout specific to this sample app
  499. String patchInstructions =
  500. "<patch>" +
  501. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
  502. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Fight</replace>" +
  503. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
  504. " <element type=\"Text\">" +
  505. " <attribute name=\"Name\" value=\"KeyBinding\" />" +
  506. " <attribute name=\"Text\" value=\"SPACE\" />" +
  507. " </element>" +
  508. " </add>" +
  509. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
  510. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Jump</replace>" +
  511. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
  512. " <element type=\"Text\">" +
  513. " <attribute name=\"Name\" value=\"KeyBinding\" />" +
  514. " <attribute name=\"Text\" value=\"UP\" />" +
  515. " </element>" +
  516. " </add>" +
  517. "</patch>";