Ver código fonte

Added spawn manager example

Björn Ritzl 2 semanas atrás
pai
commit
9f6c06f43b

BIN
factory/spawn_manager/assets/SourceSansPro-Semibold.ttf


BIN
factory/spawn_manager/assets/images/enemyBlue4.png


BIN
factory/spawn_manager/assets/images/enemyRed2.png


BIN
factory/spawn_manager/assets/images/flame.png


BIN
factory/spawn_manager/assets/images/laserRed04.png


BIN
factory/spawn_manager/assets/images/meteorGrey_tiny1.png


BIN
factory/spawn_manager/assets/images/playerShip1_red.png


BIN
factory/spawn_manager/assets/images/star2.png


BIN
factory/spawn_manager/assets/images/ufoGreen.png


+ 25 - 0
factory/spawn_manager/assets/sprites.atlas

@@ -0,0 +1,25 @@
+images {
+  image: "/assets/images/flame.png"
+}
+images {
+  image: "/assets/images/playerShip1_red.png"
+}
+images {
+  image: "/assets/images/enemyRed2.png"
+}
+images {
+  image: "/assets/images/enemyBlue4.png"
+}
+images {
+  image: "/assets/images/ufoGreen.png"
+}
+images {
+  image: "/assets/images/laserRed04.png"
+}
+images {
+  image: "/assets/images/meteorGrey_tiny1.png"
+}
+images {
+  image: "/assets/images/star2.png"
+}
+extrude_borders: 2

+ 5 - 0
factory/spawn_manager/assets/text48.font

@@ -0,0 +1,5 @@
+font: "/assets/SourceSansPro-Semibold.ttf"
+material: "/builtins/fonts/font.material"
+size: 48
+outline_alpha: 0.0
+outline_width: 0.0

BIN
factory/spawn_manager/collection.png


+ 66 - 0
factory/spawn_manager/example.md

@@ -0,0 +1,66 @@
+---
+tags: factory
+title: Spawn enemies with central management
+brief: This example shows how to spawn enemy game objects using a factory component and use a central manager to control movement and behavior of all enemies.
+author: Defold Foundation
+scripts: ship.script, spawner.script
+thumbnail: thumbnail.png
+---
+
+This example shows how to dynamically spawn enemy game objects using a factory component and use a central manager to control movement and behavior of all enemies. The setup consists of three main components: a player ship, enemy spawner, and different enemy types with customizable properties.
+
+Press keys `1`, `2`, or `3` to spawn different enemy types.
+
+Example collection consists of 2 game objects:
+
+![Screenshot showing enemy types and spawner in action](collection.png)
+
+### Ship
+The red ship at the bottom that automatically moves and shoots. Consists of:
+- A *Factory* component `bulletfactory` to spawn bullet game objects
+- A *Script* `ship` that handles automatic movement (ping-pong animation) and bullet spawning every 0.25 seconds
+- A *Sprite* component with the spaceship image
+
+Bullets are simply animated upward and automatically deleted when they reach the top.
+
+### Spawner
+Controls enemy spawning with keyboard input. Consists of:
+- A *Factory* `enemyfactory` to spawn enemies with different properties
+- A *Label* `example_description` with instructions text displayed on top
+- A *Script* `spawner` that spawns enemies.
+
+
+The spawner script defines three different enemy types: `random`, `diagonal`, and `straight`.
+Uses a factory to create enemies and store their ids and properties in a look-up table:
+
+```lua
+local properties = ENEMY_TYPES[enemy_type]
+local id factory.create("#enemyfactory", position)
+
+self.enemies[id] = { ... }
+```
+
+The spawner script handles enemy movement by looping over the list of currently spawned enemies in the `update()` lifecycle function. It also uses a physics event listener to detect any enemy to bullet collisions. The spawner script also deletes enemy objects and removes them from the look-up table.
+
+### Enemy Types
+
+**Random Enemy** (Key 1):
+- Green UFO sprite
+- 1 health point
+- Random horizontal movement that changes every second
+- Speed: 40 horizontal, -100 vertical
+
+**Diagonal Enemy** (Key 2):
+- Red enemy sprite
+- 2 health points
+- Fixed diagonal movement
+- Speed: 120 horizontal, -80 vertical
+
+**Straight Enemy** (Key 3):
+- Blue enemy sprite
+- 3 health points
+- Straight downward movement
+- Speed: 0 horizontal, -40 vertical
+
+
+Combine this example with other movement and physics examples to create a complete shoot'em up game!

+ 265 - 0
factory/spawn_manager/example/boom.particlefx

@@ -0,0 +1,265 @@
+emitters {
+  id: "light"
+  mode: PLAY_MODE_ONCE
+  duration: 1.0
+  space: EMISSION_SPACE_WORLD
+  tile_source: "/assets/sprites.atlas"
+  animation: "star2"
+  material: "/builtins/materials/particlefx.material"
+  blend_mode: BLEND_MODE_ADD
+  max_particle_count: 1
+  type: EMITTER_TYPE_CIRCLE
+  properties {
+    key: EMITTER_KEY_SPAWN_RATE
+    points {
+      y: 10.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_SIZE_X
+    points {
+      y: 8.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_SIZE_Y
+    points {
+      y: 12.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_LIFE_TIME
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_SIZE
+    points {
+      y: 20.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_RED
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_GREEN
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_BLUE
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_ALPHA
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_SCALE
+    points {
+      y: 1.0
+      t_x: 0.25919977
+      t_y: 0.9658237
+    }
+    points {
+      x: 1.0
+      y: 0.023632
+      t_x: 0.9998886
+      t_y: 0.014926955
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_RED
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_GREEN
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_BLUE
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_ALPHA
+    points {
+      y: 0.0
+      t_x: 0.07194582
+      t_y: 0.99740857
+    }
+    points {
+      x: 0.11320755
+      y: 0.99277455
+      t_x: 0.99418455
+      t_y: 0.10768964
+    }
+    points {
+      x: 0.7112546
+      y: 0.555656
+      t_x: 0.5694311
+      t_y: -0.82203907
+    }
+    points {
+      x: 1.0
+      y: 0.0072254334
+      t_x: 0.4737472
+      t_y: -0.8806609
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_ANGULAR_VELOCITY
+    points {
+      y: 1.0
+    }
+  }
+}
+emitters {
+  id: "debris"
+  mode: PLAY_MODE_ONCE
+  duration: 1.0
+  space: EMISSION_SPACE_WORLD
+  tile_source: "/assets/sprites.atlas"
+  animation: "meteorGrey_tiny1"
+  material: "/builtins/materials/particlefx.material"
+  max_particle_count: 4
+  type: EMITTER_TYPE_CIRCLE
+  properties {
+    key: EMITTER_KEY_SPAWN_RATE
+    points {
+      y: 10.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_SIZE_X
+    points {
+      y: 8.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_SIZE_Y
+    points {
+      y: 12.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_LIFE_TIME
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_SPEED
+    points {
+      y: 50.0
+    }
+    spread: 20.0
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_SIZE
+    points {
+      y: 10.0
+    }
+    spread: 5.0
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_RED
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_GREEN
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_BLUE
+    points {
+      y: 1.0
+    }
+  }
+  properties {
+    key: EMITTER_KEY_PARTICLE_ALPHA
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_SCALE
+    points {
+      y: 1.0
+      t_x: 0.25919977
+      t_y: 0.9658237
+    }
+    points {
+      x: 1.0
+      y: 0.00553
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_RED
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_GREEN
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_BLUE
+    points {
+      y: 1.0
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_ALPHA
+    points {
+      y: 0.0
+      t_x: 0.07194582
+      t_y: 0.99740857
+    }
+    points {
+      x: 0.11320755
+      y: 0.99277455
+      t_x: 0.99418455
+      t_y: 0.10768964
+    }
+    points {
+      x: 0.7112546
+      y: 0.555656
+      t_x: 0.5694311
+      t_y: -0.82203907
+    }
+    points {
+      x: 1.0
+      y: 0.0072254334
+      t_x: 0.4737472
+      t_y: -0.8806609
+    }
+  }
+  particle_properties {
+    key: PARTICLE_KEY_ANGULAR_VELOCITY
+    points {
+      y: 1.0
+    }
+  }
+}

+ 42 - 0
factory/spawn_manager/example/bullet.go

@@ -0,0 +1,42 @@
+embedded_components {
+  id: "sprite"
+  type: "sprite"
+  data: "default_animation: \"laserRed04\"\n"
+  "material: \"/builtins/materials/sprite.material\"\n"
+  "textures {\n"
+  "  sampler: \"texture_sampler\"\n"
+  "  texture: \"/assets/sprites.atlas\"\n"
+  "}\n"
+  ""
+  scale {
+    x: 0.5
+    y: 0.5
+  }
+}
+embedded_components {
+  id: "collisionobject"
+  type: "collisionobject"
+  data: "type: COLLISION_OBJECT_TYPE_TRIGGER\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_BOX\n"
+  "    position {\n"
+  "    }\n"
+  "    rotation {\n"
+  "    }\n"
+  "    index: 0\n"
+  "    count: 3\n"
+  "  }\n"
+  "  data: 3.5\n"
+  "  data: 9.0\n"
+  "  data: 10.0\n"
+  "}\n"
+  "event_collision: false\n"
+  "event_contact: false\n"
+  ""
+}

+ 45 - 0
factory/spawn_manager/example/enemy.go

@@ -0,0 +1,45 @@
+components {
+  id: "boom"
+  component: "/example/boom.particlefx"
+}
+embedded_components {
+  id: "sprite"
+  type: "sprite"
+  data: "default_animation: \"ufoGreen\"\n"
+  "material: \"/builtins/materials/sprite.material\"\n"
+  "textures {\n"
+  "  sampler: \"texture_sampler\"\n"
+  "  texture: \"/assets/sprites.atlas\"\n"
+  "}\n"
+  ""
+  scale {
+    x: 0.5
+    y: 0.5
+  }
+}
+embedded_components {
+  id: "collisionobject"
+  type: "collisionobject"
+  data: "type: COLLISION_OBJECT_TYPE_TRIGGER\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"
+  "    }\n"
+  "    rotation {\n"
+  "    }\n"
+  "    index: 0\n"
+  "    count: 1\n"
+  "    id: \"ship\"\n"
+  "  }\n"
+  "  data: 22.5\n"
+  "}\n"
+  "event_collision: false\n"
+  "event_contact: false\n"
+  ""
+}

+ 18 - 0
factory/spawn_manager/example/ship.script

@@ -0,0 +1,18 @@
+function init(self)
+	-- Animate automatic player position
+	go.animate(".", "position.x", go.PLAYBACK_LOOP_PINGPONG, 620, go.EASING_LINEAR, 6.0)
+
+	-- Create a timer to tick every 0.25 second:
+	timer.delay(0.25, true, function()
+
+		-- Create a simple bullet bullet using the factory
+		local bullet_id = factory.create("#bulletfactory", go.get_position())
+
+		-- Animate the created bullet towards top of screen, where it is deleted
+		if bullet_id then
+			go.animate(bullet_id, "position.y", go.PLAYBACK_ONCE_FORWARD, 600, go.EASING_LINEAR, 1, 0, function()
+				go.delete(bullet_id)
+			end)
+		end
+	end)
+end

+ 84 - 0
factory/spawn_manager/example/spawn_manager.collection

@@ -0,0 +1,84 @@
+name: "default"
+scale_along_z: 0
+embedded_instances {
+  id: "ship"
+  data: "components {\n"
+  "  id: \"ship\"\n"
+  "  component: \"/example/ship.script\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"sprite\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"default_animation: \\\"playerShip1_red\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "textures {\\n"
+  "  sampler: \\\"texture_sampler\\\"\\n"
+  "  texture: \\\"/assets/sprites.atlas\\\"\\n"
+  "}\\n"
+  "\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"bulletfactory\"\n"
+  "  type: \"factory\"\n"
+  "  data: \"prototype: \\\"/example/bullet.go\\\"\\n"
+  "\"\n"
+  "}\n"
+  ""
+  position {
+    x: 100.0
+    y: 50.0
+  }
+}
+embedded_instances {
+  id: "spawner"
+  data: "components {\n"
+  "  id: \"spawner\"\n"
+  "  component: \"/example/spawner.script\"\n"
+  "}\n"
+  "components {\n"
+  "  id: \"boom\"\n"
+  "  component: \"/example/boom.particlefx\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"enemyfactory\"\n"
+  "  type: \"factory\"\n"
+  "  data: \"prototype: \\\"/example/enemy.go\\\"\\n"
+  "\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"example_description\"\n"
+  "  type: \"label\"\n"
+  "  data: \"size {\\n"
+  "  x: 1024.0\\n"
+  "  y: 256.0\\n"
+  "}\\n"
+  "color {\\n"
+  "  x: 0.0\\n"
+  "  y: 0.5647059\\n"
+  "  z: 0.99215686\\n"
+  "}\\n"
+  "outline {\\n"
+  "  x: 1.0\\n"
+  "  y: 1.0\\n"
+  "  z: 1.0\\n"
+  "}\\n"
+  "text: \\\"Spawn enemies with numeric keys:\\\\n"
+  "\\\"\\n"
+  "  \\\"[1] Random [2] Diagonal [3] Straight\\\"\\n"
+  "font: \\\"/assets/text48.font\\\"\\n"
+  "material: \\\"/builtins/fonts/label.material\\\"\\n"
+  "\"\n"
+  "  position {\n"
+  "    y: 50.0\n"
+  "  }\n"
+  "  scale {\n"
+  "    x: 0.5\n"
+  "    y: 0.5\n"
+  "  }\n"
+  "}\n"
+  ""
+  position {
+    x: 360.0
+    y: 600.0
+  }
+}

+ 149 - 0
factory/spawn_manager/example/spawner.script

@@ -0,0 +1,149 @@
+-- Pre-hash some strings string since they will be used a lot
+local TRIGGER_EVENT = hash("trigger_event")
+local GROUP_BULLET = hash("bullet")
+local GROUP_ENEMY = hash("enemy")
+
+-- Define different properties for different enemies:
+local ENEMY_TYPES = {
+	random = {
+		sprite = hash("ufoGreen"),
+		health_points = 1,
+		speed = vmath.vector3(40, -100, 0),
+		is_random = true
+	},
+	diagonal = {
+		sprite = hash("enemyRed2"),
+		health_points = 2,
+		speed = vmath.vector3(120, -80, 0),
+		is_random = false
+	},
+	straight = {
+		sprite = hash("enemyBlue4"),
+		health_points = 3,
+		speed = vmath.vector3(0, -40, 0),
+		is_random = false
+	}
+}
+
+-- Helper function to spawn given enemy by its type:
+local function spawn_enemy(self, enemy_type)
+
+	-- Select properties of the enemy by type:
+	local properties = ENEMY_TYPES[enemy_type]
+
+	-- Set initial position of the spawned ship.
+	local position = go.get_position()
+
+	-- This will make the position one out of (-180, -90, 0, 90, 180):
+	position.x = position.x + math.random(-2,2) * 90
+
+	-- create enemy
+	local id = factory.create("#enemyfactory", position)
+
+	-- set animation of the sprite to the one defined by enemy properties
+	local sprite_url = msg.url(nil, id, "sprite")
+	sprite.play_flipbook(sprite_url, properties.sprite)
+
+	-- Add randomness to horizontal direction - this way enemy horizontal speed may be inverted or cleared:
+	-- -1 * self.speed.x - inverted direction
+	--  0 * self.speed.x - cleared direction
+	--  1 * self.speed.x - regular direction
+	local speed = vmath.vector3(properties.speed)
+	speed.x = speed.x * math.random(-1, 1)
+
+	-- store enemy
+	self.enemies[id] = {
+		id = id,
+		health = properties.health_points,
+		speed = speed,
+		is_random = properties.is_random,
+		timer = 1
+	}
+end
+
+function init(self)
+	-- Acquire input focus here, so we can handle inputs:
+	msg.post(".", "acquire_input_focus")
+	self.enemies = {}
+
+	-- listen to physics events
+	-- in this case we care about trigger events between
+	-- enemies and bullets
+	physics.set_event_listener(function(self, events)
+		for _,event in ipairs(events) do
+			local event_type = event.type
+			if event_type == TRIGGER_EVENT then
+				-- get the bullet id and enemy id if the colliding objects belong to
+				-- groups "bullet" and "enemy"
+				-- this is the Lua way of writing a ternary operator
+				local bullet_id = (event.a.group == GROUP_BULLET and event.a.id) or (event.b.group == GROUP_BULLET and event.b.id)
+				local enemy_id = (event.a.group == GROUP_ENEMY and event.a.id) or (event.b.group == GROUP_ENEMY and event.b.id)
+
+				-- not really necessary in this example but we might as well
+				-- double-check that the detected collision was for a bullet and enemy
+				if bullet_id and enemy_id then
+					-- remove the bullet
+					go.delete(bullet_id)
+
+					-- get the enemy from the managed enemies list
+					local enemy = self.enemies[enemy_id]
+
+					-- Remove one health point
+					enemy.health = enemy.health - 1
+
+					-- When no health points left - remove this ship
+					if enemy.health == 0 then
+						go.delete(enemy_id)
+						self.enemies[enemy_id] = nil
+
+						-- Play particlefx for damage taken:
+						particlefx.play(msg.url(nil, enemy_id, "boom"))
+					end
+				end
+			end
+		end
+	end)
+end
+
+
+function update(self, dt)
+	for id,enemy in pairs(self.enemies) do
+		-- Update enemy position based on its current speed:
+		local pos = go.get_position(id)
+		pos = pos + enemy.speed * dt
+		go.set_position(pos, id)
+
+		-- Bounce enemy off "walls":
+		if pos.x > 600 or pos.x < 50 then
+			enemy.speed.x = -enemy.speed.x
+		end
+
+		-- Remove enemy if it goes out of screen:
+		if pos.y < -50 then
+			go.delete(id)
+			self.enemies[id] = nil
+		end
+
+		-- If enemy has random movement decrease timer to
+		-- randomly switch horizontal speed every second
+		if enemy.is_random then
+			enemy.timer = enemy.timer - dt
+			if enemy.timer < 0 then
+				enemy.timer = enemy.timer + 1
+				enemy.speed.x = math.random(-1, 1) * enemy.speed.x
+			end
+		end
+	end
+end
+
+
+function on_input(self, action_id, action)
+	-- React to different key presses with spawning different enemies:
+	if action_id == hash("key_1") and action.released then
+		spawn_enemy(self, "random")
+	elseif action_id == hash("key_2") and action.released then
+		spawn_enemy(self, "diagonal")
+	elseif action_id == hash("key_3") and action.released then
+		spawn_enemy(self, "straight")
+	end
+end

+ 61 - 0
factory/spawn_manager/game.project

@@ -0,0 +1,61 @@
+[project]
+title = Spawn Manager
+
+[bootstrap]
+main_collection = /example/spawn_manager.collectionc
+
+[input]
+game_binding = /builtins/input/all.input_bindingc
+repeat_interval = 0.05
+
+[display]
+width = 720
+height = 720
+high_dpi = 1
+
+[physics]
+scale = 0.02
+gravity_y = -500.0
+
+[script]
+shared_state = 1
+
+[collection_proxy]
+max_count = 256
+
+[label]
+subpixels = 1
+
+[sprite]
+subpixels = 1
+max_count = 32765
+
+[windows]
+iap_provider = 
+
+[android]
+package = com.defold.examples
+
+[ios]
+bundle_identifier = com.defold.examples
+
+[osx]
+bundle_identifier = com.defold.examples
+
+[html5]
+show_fullscreen_button = 0
+show_made_with_defold = 0
+scale_mode = no_scale
+heap_size = 64
+
+[collection]
+max_instances = 32765
+
+[particle_fx]
+max_emitter_count = 1024
+
+[render]
+clear_color_blue = 0.2
+clear_color_green = 0.1
+clear_color_red = 0.0
+

BIN
factory/spawn_manager/thumbnail.png