diff --git a/2d/bullet_shower/README.md b/2d/bullet_shower/README.md new file mode 100644 index 00000000..e8aefc42 --- /dev/null +++ b/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) diff --git a/2d/bullet_shower/bullet.png b/2d/bullet_shower/bullet.png new file mode 100644 index 00000000..21d51e7f Binary files /dev/null and b/2d/bullet_shower/bullet.png differ diff --git a/2d/bullet_shower/bullet.png.import b/2d/bullet_shower/bullet.png.import new file mode 100644 index 00000000..252f3d4f --- /dev/null +++ b/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 diff --git a/2d/bullet_shower/bullets.gd b/2d/bullet_shower/bullets.gd new file mode 100644 index 00000000..ee1a5fe0 --- /dev/null +++ b/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() diff --git a/2d/bullet_shower/face_happy.png b/2d/bullet_shower/face_happy.png new file mode 100644 index 00000000..a50df9b7 Binary files /dev/null and b/2d/bullet_shower/face_happy.png differ diff --git a/2d/bullet_shower/face_happy.png.import b/2d/bullet_shower/face_happy.png.import new file mode 100644 index 00000000..6ebc894c --- /dev/null +++ b/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 diff --git a/2d/bullet_shower/face_sad.png b/2d/bullet_shower/face_sad.png new file mode 100644 index 00000000..07cfc39b Binary files /dev/null and b/2d/bullet_shower/face_sad.png differ diff --git a/2d/bullet_shower/face_sad.png.import b/2d/bullet_shower/face_sad.png.import new file mode 100644 index 00000000..715ac869 --- /dev/null +++ b/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 diff --git a/2d/bullet_shower/icon.png b/2d/bullet_shower/icon.png new file mode 100644 index 00000000..bc289228 Binary files /dev/null and b/2d/bullet_shower/icon.png differ diff --git a/2d/bullet_shower/icon.png.import b/2d/bullet_shower/icon.png.import new file mode 100644 index 00000000..96cbf462 --- /dev/null +++ b/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 diff --git a/2d/bullet_shower/player.gd b/2d/bullet_shower/player.gd new file mode 100644 index 00000000..c13060e4 --- /dev/null +++ b/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 diff --git a/2d/bullet_shower/project.godot b/2d/bullet_shower/project.godot new file mode 100644 index 00000000..2dc4b1fc --- /dev/null +++ b/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 ) diff --git a/2d/bullet_shower/screenshots/.gdignore b/2d/bullet_shower/screenshots/.gdignore new file mode 100644 index 00000000..e69de29b diff --git a/2d/bullet_shower/screenshots/collision.png b/2d/bullet_shower/screenshots/collision.png new file mode 100644 index 00000000..9cfbea34 Binary files /dev/null and b/2d/bullet_shower/screenshots/collision.png differ diff --git a/2d/bullet_shower/screenshots/no_collision.png b/2d/bullet_shower/screenshots/no_collision.png new file mode 100644 index 00000000..a0d63dca Binary files /dev/null and b/2d/bullet_shower/screenshots/no_collision.png differ diff --git a/2d/bullet_shower/shower.tscn b/2d/bullet_shower/shower.tscn new file mode 100644 index 00000000..6dd12a5e --- /dev/null +++ b/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"]