Ver Fonte

Port the Bullet Shower demo from Godot 2.1

This demo showcases how to use low-level Servers to achieve better
CPU performance when drawing large amounts of objects.

The code has been updated for Godot 3.2, cleaned up and has received
additional comments.
Hugo Locurcio há 4 anos atrás
pai
commit
ca0f74ee44

+ 18 - 0
2d/bullet_shower/README.md

@@ -0,0 +1,18 @@
+# Bullet Shower
+
+This demonstrates how to manage large amounts of objects efficiently using
+low-level Servers.
+
+See
+[Optimization using Servers](https://docs.godotengine.org/en/latest/tutorials/performance/using_servers.html)
+in the documentation for more information.
+
+Language: GDScript
+
+Renderer: GLES 2
+
+## Screenshots
+
+![No collision](screenshots/no_collision.png)
+
+![Collision](screenshots/collision.png)

BIN
2d/bullet_shower/bullet.png


+ 34 - 0
2d/bullet_shower/bullet.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/bullet.png-ff1424653e10246c11e3724e402c519e.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://bullet.png"
+dest_files=[ "res://.import/bullet.png-ff1424653e10246c11e3724e402c519e.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0

+ 86 - 0
2d/bullet_shower/bullets.gd

@@ -0,0 +1,86 @@
+extends Node2D
+# This demo is an example of controling a high number of 2D objects with logic
+# and collision without using nodes in the scene. This technique is a lot more
+# efficient than using instancing and nodes, but requires more programming and
+# is less visual. Bullets are managed together in the `bullets.gd` script.
+
+const BULLET_COUNT = 500
+const SPEED_MIN = 20
+const SPEED_MAX = 80
+
+const bullet_image = preload("res://bullet.png")
+
+var bullets = []
+var shape
+
+
+class Bullet:
+	var position = Vector2()
+	var speed = 1.0
+	# The body is stored as a RID, which is an "opaque" way to access resources.
+	# With large amounts of objects (thousands or more), it can be significantly
+	# faster to use RIDs compared to a high-level approach.
+	var body = RID()
+
+
+func _ready():
+	randomize()
+
+	shape = Physics2DServer.circle_shape_create()
+	# Set the collision shape's radius for each bullet in pixels.
+	Physics2DServer.shape_set_data(shape, 8)
+
+	for _i in BULLET_COUNT:
+		var bullet = Bullet.new()
+		# Give each bullet its own speed.
+		bullet.speed = rand_range(SPEED_MIN, SPEED_MAX)
+		bullet.body = Physics2DServer.body_create()
+
+		Physics2DServer.body_set_space(bullet.body, get_world_2d().get_space())
+		Physics2DServer.body_add_shape(bullet.body, shape)
+
+		# Place bullets randomly on the viewport and move bullets outside the
+		# play area so that they fade in nicely.
+		bullet.position = Vector2(
+			rand_range(0, get_viewport_rect().size.x) + get_viewport_rect().size.x,
+			rand_range(0, get_viewport_rect().size.y)
+		)
+		var transform2d = Transform2D()
+		transform2d.origin = bullet.position
+		Physics2DServer.body_set_state(bullet.body, Physics2DServer.BODY_STATE_TRANSFORM, transform2d)
+
+		bullets.push_back(bullet)
+
+
+func _process(delta):
+	var transform2d = Transform2D()
+	for bullet in bullets:
+		bullet.position.x -= bullet.speed * delta
+
+		if bullet.position.x < -16:
+			# The bullet has left the screen; move it back to the right.
+			bullet.position.x = get_viewport_rect().size.x + 16
+
+		transform2d.origin = bullet.position
+
+		Physics2DServer.body_set_state(bullet.body, Physics2DServer.BODY_STATE_TRANSFORM, transform2d)
+
+	# Order the CanvasItem to update since bullets are moving every frame.
+	update()
+
+
+# Instead of drawing each bullet individually in a script attached to each bullet,
+# we are drawing *all* the bullets at once here.
+func _draw():
+	var offset = -bullet_image.get_size() * 0.5
+	for bullet in bullets:
+		draw_texture(bullet_image, bullet.position + offset)
+
+
+# Perform cleanup operations (required to exit without error messages in the console).
+func _exit_tree():
+	for bullet in bullets:
+		Physics2DServer.free_rid(bullet.body)
+
+	Physics2DServer.free_rid(shape)
+	bullets.clear()

BIN
2d/bullet_shower/face_happy.png


+ 34 - 0
2d/bullet_shower/face_happy.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/face_happy.png-38d387d31ec13459f749c93ce3d75d80.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://face_happy.png"
+dest_files=[ "res://.import/face_happy.png-38d387d31ec13459f749c93ce3d75d80.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0

BIN
2d/bullet_shower/face_sad.png


+ 34 - 0
2d/bullet_shower/face_sad.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/face_sad.png-0ac7165eab24f595aba17a746a66c550.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://face_sad.png"
+dest_files=[ "res://.import/face_sad.png-0ac7165eab24f595aba17a746a66c550.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0

BIN
2d/bullet_shower/icon.png


+ 34 - 0
2d/bullet_shower/icon.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.png"
+dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0

+ 33 - 0
2d/bullet_shower/player.gd

@@ -0,0 +1,33 @@
+extends Node2D
+# This demo is an example of controling a high number of 2D objects with logic
+# and collision without using nodes in the scene. This technique is a lot more
+# efficient than using instancing and nodes, but requires more programming and
+# is less visual. Bullets are managed together in the `bullets.gd` script.
+
+# The number of bullets currently touched by the player.
+var touching = 0
+
+onready var sprite = $AnimatedSprite
+
+
+func _ready():
+	# The player follows the mouse cursor automatically, so there's no point
+	# in displaying the mouse cursor.
+	Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
+
+
+func _input(event):
+	if event is InputEventMouseMotion:
+		position = event.position - Vector2(0, 16)
+
+
+func _on_body_shape_entered(_body_id, _body, _body_shape, _local_shape):
+	touching += 1
+	if touching >= 1:
+		sprite.frame = 1
+
+
+func _on_body_shape_exited(_body_id, _body, _body_shape, _local_shape):
+	touching -= 1
+	if touching == 0:
+		sprite.frame = 0

+ 41 - 0
2d/bullet_shower/project.godot

@@ -0,0 +1,41 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+;   [section] ; section goes between []
+;   param=value ; assign values to parameters
+
+config_version=4
+
+_global_script_classes=[  ]
+_global_script_class_icons={
+
+}
+
+[application]
+
+config/name="Bullet Shower"
+config/description="Demonstrates how to manage large amounts of objects efficiently using low-level Servers."
+run/main_scene="res://shower.tscn"
+config/icon="res://icon.png"
+
+[display]
+
+window/dpi/allow_hidpi=true
+window/stretch/mode="2d"
+window/stretch/aspect="expand"
+
+[physics]
+
+2d/cell_size=64
+common/enable_pause_aware_picking=true
+
+[rendering]
+
+quality/driver/driver_name="GLES2"
+quality/intended_usage/framebuffer_allocation=0
+quality/intended_usage/framebuffer_allocation.mobile=0
+vram_compression/import_etc=true
+vram_compression/import_etc2=false
+environment/default_clear_color=Color( 0.133333, 0.133333, 0.2, 1 )

+ 0 - 0
2d/bullet_shower/screenshots/.gdignore


BIN
2d/bullet_shower/screenshots/collision.png


BIN
2d/bullet_shower/screenshots/no_collision.png


+ 33 - 0
2d/bullet_shower/shower.tscn

@@ -0,0 +1,33 @@
+[gd_scene load_steps=7 format=2]
+
+[ext_resource path="res://bullets.gd" type="Script" id=2]
+[ext_resource path="res://face_happy.png" type="Texture" id=3]
+[ext_resource path="res://face_sad.png" type="Texture" id=4]
+[ext_resource path="res://player.gd" type="Script" id=5]
+
+[sub_resource type="SpriteFrames" id=1]
+animations = [ {
+"frames": [ ExtResource( 3 ), ExtResource( 4 ) ],
+"loop": true,
+"name": "default",
+"speed": 5.0
+} ]
+
+[sub_resource type="CircleShape2D" id=2]
+radius = 27.0
+
+[node name="Shower" type="Node2D"]
+
+[node name="Bullets" type="Node2D" parent="."]
+script = ExtResource( 2 )
+
+[node name="Player" type="Area2D" parent="."]
+script = ExtResource( 5 )
+
+[node name="AnimatedSprite" type="AnimatedSprite" parent="Player"]
+frames = SubResource( 1 )
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="Player"]
+shape = SubResource( 2 )
+[connection signal="body_shape_entered" from="Player" to="Player" method="_on_body_shape_entered"]
+[connection signal="body_shape_exited" from="Player" to="Player" method="_on_body_shape_exited"]