فهرست منبع

Added bullet and knockback examples

Björn Ritzl 3 سال پیش
والد
کامیت
40501388d8

BIN
assets/images/enemyBlack1.png


BIN
assets/images/playerShip1_red.png


+ 8 - 0
assets/sprites.atlas

@@ -66,6 +66,14 @@ images {
   image: "/assets/images/green_button03.png"
   image: "/assets/images/green_button03.png"
   sprite_trim_mode: SPRITE_TRIM_MODE_OFF
   sprite_trim_mode: SPRITE_TRIM_MODE_OFF
 }
 }
+images {
+  image: "/assets/images/playerShip1_red.png"
+  sprite_trim_mode: SPRITE_TRIM_MODE_OFF
+}
+images {
+  image: "/assets/images/enemyBlack1.png"
+  sprite_trim_mode: SPRITE_TRIM_MODE_OFF
+}
 animations {
 animations {
   id: "bee"
   id: "bee"
   images {
   images {

+ 38 - 2
examples/_main/loader.go

@@ -209,9 +209,9 @@ embedded_components {
   }
   }
 }
 }
 embedded_components {
 embedded_components {
-  id: "basics/spawn"
+  id: "factory/basic"
   type: "collectionproxy"
   type: "collectionproxy"
-  data: "collection: \"/examples/basics/spawn/spawn.collection\"\n"
+  data: "collection: \"/examples/factory/basic/basicfactory.collection\"\n"
   "exclude: false\n"
   "exclude: false\n"
   ""
   ""
   position {
   position {
@@ -910,3 +910,39 @@ embedded_components {
     w: 1.0
     w: 1.0
   }
   }
 }
 }
+embedded_components {
+  id: "factory/bullets"
+  type: "collectionproxy"
+  data: "collection: \"/examples/factory/bullets/bullets.collection\"\n"
+  "exclude: false\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}
+embedded_components {
+  id: "physics/knockback"
+  type: "collectionproxy"
+  data: "collection: \"/examples/physics/knockback/knockback.collection\"\n"
+  "exclude: false\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}

+ 3 - 2
examples/_main/menu.gui_script

@@ -100,9 +100,10 @@ end
 
 
 function init(self)
 function init(self)
 	self.index = {}
 	self.index = {}
-	self.index["basics"] = { "message_passing", "parent_child", "spawn", "z_order" }
+	self.index["basics"] = { "message_passing", "parent_child", "z_order" }
+	self.index["factory"] = { "basic", "bullets" }
 	self.index["movement"] = { "simple_move", "follow", "move_to", "move_forward", "movement_speed", "look_at" }
 	self.index["movement"] = { "simple_move", "follow", "move_to", "move_forward", "movement_speed", "look_at" }
-	self.index["physics"] = { "dynamic", "kinematic", "raycast", "trigger", "hinge_joint", "pendulum"}
+	self.index["physics"] = { "dynamic", "kinematic", "raycast", "trigger", "hinge_joint", "pendulum", "knockback"}
 	self.index["animation"] = { "spinner", "flipbook", "chained_tween", "basic_tween", "spine", "cursor" }
 	self.index["animation"] = { "spinner", "flipbook", "chained_tween", "basic_tween", "spine", "cursor" }
 	self.index["gui"] = { "button", "stencil", "load_texture", "progress", "pointer_over", "color", "slice9" }
 	self.index["gui"] = { "button", "stencil", "load_texture", "progress", "pointer_over", "color", "slice9" }
 	self.index["input"] = { "move", "text", "down_duration", "mouse_and_touch" }
 	self.index["input"] = { "move", "text", "down_duration", "mouse_and_touch" }

+ 2 - 2
examples/basics/spawn/spawn.collection → examples/factory/basic/basicfactory.collection

@@ -4,7 +4,7 @@ embedded_instances {
   id: "bunny"
   id: "bunny"
   data: "components {\n"
   data: "components {\n"
   "  id: \"script\"\n"
   "  id: \"script\"\n"
-  "  component: \"/examples/basics/spawn/bunny.script\"\n"
+  "  component: \"/examples/factory/basic/bunny.script\"\n"
   "  position {\n"
   "  position {\n"
   "    x: 0.0\n"
   "    x: 0.0\n"
   "    y: 0.0\n"
   "    y: 0.0\n"
@@ -94,7 +94,7 @@ embedded_instances {
   "embedded_components {\n"
   "embedded_components {\n"
   "  id: \"carrotfactory\"\n"
   "  id: \"carrotfactory\"\n"
   "  type: \"factory\"\n"
   "  type: \"factory\"\n"
-  "  data: \"prototype: \\\"/examples/basics/spawn/carrot.go\\\"\\n"
+  "  data: \"prototype: \\\"/examples/factory/basic/carrot.go\\\"\\n"
   "load_dynamically: false\\n"
   "load_dynamically: false\\n"
   "\"\n"
   "\"\n"
   "  position {\n"
   "  position {\n"

+ 7 - 5
examples/basics/spawn/bunny.script → examples/factory/basic/bunny.script

@@ -4,19 +4,21 @@ end
 
 
 function on_input(self, action_id, action)
 function on_input(self, action_id, action)
 	if action_id == hash("touch") and action.pressed then -- <2>
 	if action_id == hash("touch") and action.pressed then -- <2>
-		local pos = go.get_position()
-		pos.x = pos.x + 100 -- <3>
+		local pos = vmath.vector3(action.x, action.y, 0) -- <3>
 		local carrot_id = factory.create("#carrotfactory", pos) -- <4>
 		local carrot_id = factory.create("#carrotfactory", pos) -- <4>
-		go.animate(carrot_id, "position.x", go.PLAYBACK_ONCE_FORWARD, 720, go.EASING_LINEAR, 3) -- <5>
+		go.animate(carrot_id, "euler.z", go.PLAYBACK_ONCE_FORWARD, 360, go.EASING_LINEAR, 1, 0, function()   -- <5>
+			go.delete(carrot_id)  -- <6>
+		end)
 	end
 	end
 end
 end
 
 
 --[[
 --[[
 1. Acquire input focus so we get input from the engine.
 1. Acquire input focus so we get input from the engine.
 2. If the user clicks.
 2. If the user clicks.
-3. Calculate a spawning position.
+3. Set the spawning position to the mouse click position.
 4. Tell the component "carrotfactory" ("#" denotes a component in the
 4. Tell the component "carrotfactory" ("#" denotes a component in the
    current game object) to spawn a game object according to its prototype.
    current game object) to spawn a game object according to its prototype.
    The function returns the id of the new game object.
    The function returns the id of the new game object.
-5. Animate the position of the new game object.
+5. Rotate the new game object.
+6. Delete the game object
 --]]
 --]]

+ 0 - 15
examples/basics/spawn/carrot.go → examples/factory/basic/carrot.go

@@ -1,18 +1,3 @@
-components {
-  id: "script"
-  component: "/examples/basics/spawn/carrot.script"
-  position {
-    x: 0.0
-    y: 0.0
-    z: 0.0
-  }
-  rotation {
-    x: 0.0
-    y: 0.0
-    z: 0.0
-    w: 1.0
-  }
-}
 embedded_components {
 embedded_components {
   id: "sprite"
   id: "sprite"
   type: "sprite"
   type: "sprite"

+ 0 - 0
examples/basics/spawn/carrot.png → examples/factory/basic/carrot.png


+ 0 - 0
examples/basics/spawn/carrot.script → examples/factory/basic/carrot.script


+ 0 - 0
examples/basics/spawn/spawn.md → examples/factory/basic/spawn.md


+ 0 - 0
examples/basics/spawn/spawn.png → examples/factory/basic/spawn.png


+ 20 - 0
examples/factory/bullets/bullet.go

@@ -0,0 +1,20 @@
+embedded_components {
+  id: "sprite"
+  type: "sprite"
+  data: "tile_set: \"/assets/sprites.atlas\"\n"
+  "default_animation: \"flame\"\n"
+  "material: \"/builtins/materials/sprite.material\"\n"
+  "blend_mode: BLEND_MODE_ALPHA\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}

+ 149 - 0
examples/factory/bullets/bullets.collection

@@ -0,0 +1,149 @@
+name: "default"
+scale_along_z: 0
+embedded_instances {
+  id: "go"
+  data: "embedded_components {\n"
+  "  id: \"label1\"\n"
+  "  type: \"label\"\n"
+  "  data: \"size {\\n"
+  "  x: 128.0\\n"
+  "  y: 32.0\\n"
+  "  z: 0.0\\n"
+  "  w: 0.0\\n"
+  "}\\n"
+  "scale {\\n"
+  "  x: 0.5\\n"
+  "  y: 0.5\\n"
+  "  z: 1.0\\n"
+  "  w: 0.0\\n"
+  "}\\n"
+  "color {\\n"
+  "  x: 0.0\\n"
+  "  y: 0.5647059\\n"
+  "  z: 0.99215686\\n"
+  "  w: 1.0\\n"
+  "}\\n"
+  "outline {\\n"
+  "  x: 1.0\\n"
+  "  y: 1.0\\n"
+  "  z: 1.0\\n"
+  "  w: 1.0\\n"
+  "}\\n"
+  "shadow {\\n"
+  "  x: 0.0\\n"
+  "  y: 0.0\\n"
+  "  z: 0.0\\n"
+  "  w: 1.0\\n"
+  "}\\n"
+  "leading: 1.0\\n"
+  "tracking: 0.0\\n"
+  "pivot: PIVOT_CENTER\\n"
+  "blend_mode: BLEND_MODE_ALPHA\\n"
+  "line_break: false\\n"
+  "text: \\\"Click/spacebar to pew pew!\\\"\\n"
+  "font: \\\"/assets/text48.font\\\"\\n"
+  "material: \\\"/builtins/fonts/label.material\\\"\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: 360.0\n"
+  "    y: 20.0\n"
+  "    z: 0.0\n"
+  "  }\n"
+  "  rotation {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "    w: 1.0\n"
+  "  }\n"
+  "}\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+  scale3 {
+    x: 1.0
+    y: 1.0
+    z: 1.0
+  }
+}
+embedded_instances {
+  id: "player"
+  data: "components {\n"
+  "  id: \"player\"\n"
+  "  component: \"/examples/factory/bullets/player.script\"\n"
+  "  position {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "  }\n"
+  "  rotation {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "    w: 1.0\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"bulletfactory\"\n"
+  "  type: \"factory\"\n"
+  "  data: \"prototype: \\\"/examples/factory/bullets/bullet.go\\\"\\n"
+  "load_dynamically: false\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "  }\n"
+  "  rotation {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "    w: 1.0\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"sprite\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"tile_set: \\\"/assets/sprites.atlas\\\"\\n"
+  "default_animation: \\\"playerShip1_red\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "blend_mode: BLEND_MODE_ALPHA\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "  }\n"
+  "  rotation {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "    w: 1.0\n"
+  "  }\n"
+  "}\n"
+  ""
+  position {
+    x: 360.0
+    y: 100.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+  scale3 {
+    x: 1.0
+    y: 1.0
+    z: 1.0
+  }
+}

+ 9 - 0
examples/factory/bullets/bullets.md

@@ -0,0 +1,9 @@
+---
+title: Shoot bullets
+brief: This example shows how to dynamically spawn bullet game objects using a factory component.
+scripts: player.script
+---
+
+This example shows how to dynamically spawn bullet game objects using a factory component and how to also move and delete the bullets.
+
+Combine this example with some of the examples from the movement and physics categories to create a shoot 'em up game!

+ 53 - 0
examples/factory/bullets/player.go

@@ -0,0 +1,53 @@
+components {
+  id: "player"
+  component: "/examples/factory/bullets/player.script"
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}
+embedded_components {
+  id: "sprite"
+  type: "sprite"
+  data: "tile_set: \"/assets/sprites.atlas\"\n"
+  "default_animation: \"playerShip1_red\"\n"
+  "material: \"/builtins/materials/sprite.material\"\n"
+  "blend_mode: BLEND_MODE_ALPHA\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}
+embedded_components {
+  id: "bulletfactory"
+  type: "factory"
+  data: "prototype: \"/examples/factory/bullets/bullet.go\"\n"
+  "load_dynamically: false\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}

+ 26 - 0
examples/factory/bullets/player.script

@@ -0,0 +1,26 @@
+function init(self)
+	-- make sure the script will receive user input
+	msg.post(".", "acquire_input_focus")
+end
+
+function on_input(self, action_id, action)
+	-- mouse or spacebar
+	if (action_id == hash("touch") or action_id == hash("action")) and action.pressed then
+		-- position bullet somewhat offset from the player position
+		local pos = go.get_position()
+		pos.y = pos.y + 50
+
+		-- spawn a bullet
+		local bullet_id = factory.create("#bulletfactory", pos)
+
+		-- animate the bullet
+		local distance = 1000					-- distance in pixels
+		local speed = 800						-- pixels per second
+		local duration = distance / speed		-- time in second to travel the full distance
+		local to = pos.y + distance
+		-- start animation and delete bullet when it has reached its destination
+		go.animate(bullet_id, "position.y", go.PLAYBACK_ONCE_FORWARD, to, go.EASING_LINEAR, duration, 0, function()
+			go.delete(bullet_id)
+		end)
+	end
+end

+ 1 - 1
examples/movement/look_at/look_at.collection

@@ -95,7 +95,7 @@ embedded_instances {
   "pivot: PIVOT_CENTER\\n"
   "pivot: PIVOT_CENTER\\n"
   "blend_mode: BLEND_MODE_ALPHA\\n"
   "blend_mode: BLEND_MODE_ALPHA\\n"
   "line_break: false\\n"
   "line_break: false\\n"
-  "text: \\\"Move with arrow keys (left+right = rotate, up = forward)\\\"\\n"
+  "text: \\\"I will always look at  the mouse pointer\\\"\\n"
   "font: \\\"/assets/text48.font\\\"\\n"
   "font: \\\"/assets/text48.font\\\"\\n"
   "material: \\\"/builtins/fonts/label.material\\\"\\n"
   "material: \\\"/builtins/fonts/label.material\\\"\\n"
   "\"\n"
   "\"\n"

+ 2 - 0
examples/movement/look_at/look_at.md

@@ -3,3 +3,5 @@ title: Look at
 brief: This example shows how to rotate a game object to look at the mouse cursor
 brief: This example shows how to rotate a game object to look at the mouse cursor
 scripts: look_at.script
 scripts: look_at.script
 ---
 ---
+
+This example shows how to rotate a game object to look at the mouse cursor. It reads the mouse position in `on_input` and uses the mathematical function `math.atan2(x, y)` to calculate the angle between the ray to the point to look at and the positive x-axis. This angle is used to set the rotation of the game object to always look at the mouse position. 

+ 66 - 0
examples/physics/knockback/bullet.go

@@ -0,0 +1,66 @@
+embedded_components {
+  id: "sprite"
+  type: "sprite"
+  data: "tile_set: \"/assets/sprites.atlas\"\n"
+  "default_animation: \"flame\"\n"
+  "material: \"/builtins/materials/sprite.material\"\n"
+  "blend_mode: BLEND_MODE_ALPHA\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}
+embedded_components {
+  id: "collisionobject"
+  type: "collisionobject"
+  data: "collision_shape: \"\"\n"
+  "type: COLLISION_OBJECT_TYPE_KINEMATIC\n"
+  "mass: 0.0\n"
+  "friction: 0.1\n"
+  "restitution: 0.5\n"
+  "group: \"bullet\"\n"
+  "mask: \"enemy\"\n"
+  "embedded_collision_shape {\n"
+  "  shapes {\n"
+  "    shape_type: TYPE_SPHERE\n"
+  "    position {\n"
+  "      x: 0.0\n"
+  "      y: 20.0\n"
+  "      z: 0.0\n"
+  "    }\n"
+  "    rotation {\n"
+  "      x: 0.0\n"
+  "      y: 0.0\n"
+  "      z: 0.0\n"
+  "      w: 1.0\n"
+  "    }\n"
+  "    index: 0\n"
+  "    count: 1\n"
+  "  }\n"
+  "  data: 20.0\n"
+  "}\n"
+  "linear_damping: 0.0\n"
+  "angular_damping: 0.0\n"
+  "locked_rotation: false\n"
+  "bullet: false\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}

+ 81 - 0
examples/physics/knockback/enemy.go

@@ -0,0 +1,81 @@
+components {
+  id: "enemy"
+  component: "/examples/physics/knockback/enemy.script"
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}
+embedded_components {
+  id: "sprite"
+  type: "sprite"
+  data: "tile_set: \"/assets/sprites.atlas\"\n"
+  "default_animation: \"enemyBlack1\"\n"
+  "material: \"/builtins/materials/sprite.material\"\n"
+  "blend_mode: BLEND_MODE_ALPHA\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}
+embedded_components {
+  id: "collisionobject"
+  type: "collisionobject"
+  data: "collision_shape: \"\"\n"
+  "type: COLLISION_OBJECT_TYPE_KINEMATIC\n"
+  "mass: 0.0\n"
+  "friction: 0.1\n"
+  "restitution: 0.5\n"
+  "group: \"enemy\"\n"
+  "mask: \"bullet\"\n"
+  "embedded_collision_shape {\n"
+  "  shapes {\n"
+  "    shape_type: TYPE_SPHERE\n"
+  "    position {\n"
+  "      x: 0.0\n"
+  "      y: 0.0\n"
+  "      z: 0.0\n"
+  "    }\n"
+  "    rotation {\n"
+  "      x: 0.0\n"
+  "      y: 0.0\n"
+  "      z: 0.0\n"
+  "      w: 1.0\n"
+  "    }\n"
+  "    index: 0\n"
+  "    count: 1\n"
+  "  }\n"
+  "  data: 40.0\n"
+  "}\n"
+  "linear_damping: 0.0\n"
+  "angular_damping: 0.0\n"
+  "locked_rotation: false\n"
+  "bullet: false\n"
+  ""
+  position {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+}

+ 29 - 0
examples/physics/knockback/enemy.script

@@ -0,0 +1,29 @@
+-- move game object back and forth from the current position to a target position
+local function move()
+	local pos = go.get_position()
+	local to = vmath.vector3(pos.x, 300, 0)
+	local distance = pos.y - to.y
+	local speed = 40
+	local duration = distance / speed
+	go.animate(".", "position", go.PLAYBACK_LOOP_PINGPONG, to, go.EASING_INOUTQUAD, duration)
+end
+
+function init(self)
+	move()
+end
+
+function on_message(self, message_id, message, sender)
+	if message_id == hash("contact_point_response") then
+		if message.other_group == hash("bullet") then
+			-- delete the bullet
+			go.delete(message.other_id)
+
+			-- get the position of the game object
+			local pos = go.get_position()
+			-- set a pushback direction based on the collision normal
+			local to = pos + message.normal * 30
+			-- knockback animation, then continue moving
+			go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, to, go.EASING_OUTQUAD, 0.1, 0, move)
+		end
+	end
+end

+ 95 - 0
examples/physics/knockback/knockback.collection

@@ -0,0 +1,95 @@
+name: "default"
+instances {
+  id: "enemy"
+  prototype: "/examples/physics/knockback/enemy.go"
+  position {
+    x: 360.0
+    y: 456.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+  scale3 {
+    x: 1.0
+    y: 1.0
+    z: 1.0
+  }
+}
+scale_along_z: 0
+embedded_instances {
+  id: "player"
+  data: "components {\n"
+  "  id: \"player\"\n"
+  "  component: \"/examples/physics/knockback/player.script\"\n"
+  "  position {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "  }\n"
+  "  rotation {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "    w: 1.0\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"bulletfactory\"\n"
+  "  type: \"factory\"\n"
+  "  data: \"prototype: \\\"/examples/physics/knockback/bullet.go\\\"\\n"
+  "load_dynamically: false\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "  }\n"
+  "  rotation {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "    w: 1.0\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"sprite\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"tile_set: \\\"/assets/sprites.atlas\\\"\\n"
+  "default_animation: \\\"playerShip1_red\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "blend_mode: BLEND_MODE_ALPHA\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "  }\n"
+  "  rotation {\n"
+  "    x: 0.0\n"
+  "    y: 0.0\n"
+  "    z: 0.0\n"
+  "    w: 1.0\n"
+  "  }\n"
+  "}\n"
+  ""
+  position {
+    x: 360.0
+    y: 100.0
+    z: 0.0
+  }
+  rotation {
+    x: 0.0
+    y: 0.0
+    z: 0.0
+    w: 1.0
+  }
+  scale3 {
+    x: 1.0
+    y: 1.0
+    z: 1.0
+  }
+}

+ 24 - 0
examples/physics/knockback/knockback.md

@@ -0,0 +1,24 @@
+---
+title: Knockback
+brief: This example shows how to create a knockback effect when hit.
+scripts: enemy.script
+---
+
+This example shows how to create a knockback effect when hit. The setup consists of three game objects; one for the player, one for the enemy and one for the bullet that is spawned using a factory (see example on how to spawn bullets).
+
+player
+: The red ship at the bottom. Contains:
+  - A *Sprite* component with the spaceship image.
+  - A *Factory* component to spawn bullet game objects
+  - A script to handle spawning of bullets.
+
+bullet
+: The bullet fired by the player. Contains:
+  - A *Sprite* component with a bullet image.
+  - A *Collision object* component. *Type* is set to `KINEMATIC`. It has a sphere *Shape* matching image.
+
+enemy
+: The black ship at the top. Contains:
+  - A *Sprite* component with the spaceship image.
+  - A *Collision object* component. *Type* is set to `KINEMATIC`. It has a sphere *Shape* matching image.
+  - A script to handle collisions with bullets.

+ 26 - 0
examples/physics/knockback/player.script

@@ -0,0 +1,26 @@
+function init(self)
+	-- make sure the script will receive user input
+	msg.post(".", "acquire_input_focus")
+end
+
+function on_input(self, action_id, action)
+	-- mouse or spacebar
+	if (action_id == hash("touch") or action_id == hash("action")) and action.pressed then
+		-- position bullet somewhat offset from the player position
+		local pos = go.get_position()
+		pos.y = pos.y + 50
+
+		-- spawn a bullet
+		local bullet_id = factory.create("#bulletfactory", pos)
+
+		-- animate the bullet
+		local distance = 1000					-- distance in pixels
+		local speed = 800						-- pixels per second
+		local duration = distance / speed		-- time in second to travel the full distance
+		local to = pos.y + distance
+		-- start animation and delete bullet when it has reached its destination
+		go.animate(bullet_id, "position.y", go.PLAYBACK_ONCE_FORWARD, to, go.EASING_LINEAR, duration, 0, function()
+			go.delete(bullet_id)
+		end)
+	end
+end

+ 0 - 0
assets/tiles/tilesource.png → examples/tilemap/collisions/tilesource.png


+ 4 - 0
input/game.input_binding

@@ -18,6 +18,10 @@ key_trigger {
   input: KEY_BACKSPACE
   input: KEY_BACKSPACE
   action: "backspace"
   action: "backspace"
 }
 }
+key_trigger {
+  input: KEY_SPACE
+  action: "action"
+}
 mouse_trigger {
 mouse_trigger {
   input: MOUSE_BUTTON_LEFT
   input: MOUSE_BUTTON_LEFT
   action: "touch"
   action: "touch"