diff --git a/Battle Pong/Ball.gd b/Battle Pong/Ball.gd index ae707220fd4e87e478dd453a9e4e945c1ced3272..d56d1fd650da0c555fa91e8d8e91f60dee135985 100644 --- a/Battle Pong/Ball.gd +++ b/Battle Pong/Ball.gd @@ -41,7 +41,8 @@ func run(delta): ball_hit_paddle(get_tree().get_root().find_node("PlayerOne", true, false).position, get_tree().get_root().find_node("PlayerOne", true, false).get_shape(), true) elif collision.collider.name == "PlayerTwo": ball_hit_paddle(get_tree().get_root().find_node("PlayerTwo", true, false).position, get_tree().get_root().find_node("PlayerTwo", true, false).get_shape(), false) - + elif collision.collider.name =="Obstacle": + velocity = velocity.bounce(collision.normal) pass # velocity.x *=-1 # var localCollisionPos = collision.Position - collision.collider.Position; diff --git a/Battle Pong/GameSettings.gd b/Battle Pong/GameSettings.gd index a3e92d4cc6ade797867dcc1fec984402955d07db..da6f1688d96568ecbf930df160b6f2d6b175e10b 100644 --- a/Battle Pong/GameSettings.gd +++ b/Battle Pong/GameSettings.gd @@ -4,6 +4,7 @@ export var rendering_enabled = false export var learn_with_images = true export var trainings_mode_enabled = false export var local_two_player = false +export var obstacles_enabled = false # Constant values !!!! Keep always up to date !!! var display_window_width = 1024 @@ -34,6 +35,9 @@ var player_one_speed var player_two_length var player_two_speed +var obstacle_length +var obstacle_speed + var path="data.json" var default_data = { @@ -67,6 +71,10 @@ var default_data = { "player_two":{ "length":60, "speed":300 + }, + "obstacle":{ + "length":60, + "speed":500 } } @@ -74,12 +82,12 @@ var data = default_data # Called when the node enters the scene tree for the first time. func _ready(): + reset_data() # Initialize Values load_data() pass # Replace with function body. func load_data(): var file = File.new() - if not file.file_exists(path): reset_data() return @@ -126,6 +134,10 @@ func save_data(): "player_two":{ "length":$"/root/GameSettings".player_two_length, "speed":$"/root/GameSettings".player_two_speed + }, + "obstacle":{ + "length":$"/root/GameSettings".obstacle_length, + "speed":$"/root/GameSettings".obstacle_speed } } var file @@ -139,33 +151,65 @@ func reset_data(): update_settings() func update_settings(): - game_playtime_per_step = data["game"]["playtime_per_step"] as float - game_wins_to_reset = data["game"]["wins_to_reset"] as int - game_port= data["game"]["port"] as int - - var format = data["image"]["format"] - if format =="RGB8": - image_rgb =true - else: - image_rgb = false; - image_heigth = data["image"]["height"] as int - image_width = data["image"]["width"] as int - - trainer_ip = data["trainer"]["ip"] - trainer_port = data["trainer"]["port"] as int - trainer_position = data["trainer"]["position"] - trainer_realtime_enabled = data["trainer"]["realtime_enabled"] as bool - - ball_height =data["ball"]["height"] as int - ball_width = data["ball"]["width"] as int - ball_speed_min = data["ball"]["speed_min"] as int - ball_speed_max = data["ball"]["speed_max"] as int - ball_speed_increment = data["ball"]["speed_increment"] as int - - player_one_length = data["player_one"]["length"] as int - player_one_speed = data["player_one"]["speed"] as int - - player_two_length = data["player_two"]["length"] as int - player_two_speed = data["player_two"]["speed"] as int + if data.has("game"): + if data["game"].has("playtime_per_step"): + game_playtime_per_step = data["game"]["playtime_per_step"] as float + if data["game"].has("wins_to_reset"): + game_wins_to_reset = data["game"]["wins_to_reset"] as int + if data["game"].has("port"): + game_port= data["game"]["port"] as int + + if data.has("image"): + if data["image"].has("format"): + var format = data["image"]["format"] + if format == "RGB8": + image_rgb =true + else: + image_rgb = false; + if data["image"].has("height"): + image_heigth = data["image"]["height"] as int + if data["image"].has("width"): + image_width = data["image"]["width"] as int + + + if data.has("trainer"): + if data["trainer"].has("ip"): + trainer_ip = data["trainer"]["ip"] + if data["trainer"].has("ip"): + trainer_port = data["trainer"]["ip"] as int + if data["trainer"].has("position"): + trainer_position = data["trainer"]["position"] + if data["trainer"].has("realtime_enabled"): + trainer_realtime_enabled = data["trainer"]["realtime_enabled"] as bool + + if data.has("ball"): + if data["ball"].has("height"): + ball_height =data["ball"]["height"] as int + if data["ball"].has("width"): + ball_width = data["ball"]["width"] as int + if data["ball"].has("speed_min"): + ball_speed_min = data["ball"]["speed_min"] as int + if data["ball"].has("speed_max"): + ball_speed_max = data["ball"]["speed_max"] as int + if data["ball"].has("speed_increment"): + ball_speed_increment = data["ball"]["speed_increment"] as int + + if data.has("player_one"): + if data["player_one"].has("length"): + player_one_length = data["player_one"]["length"] as int + if data["player_one"].has("speed"): + player_one_speed = data["player_one"]["speed"] as int + + if data.has("player_two"): + if data["player_two"].has("length"): + player_two_length = data["player_two"]["length"] as int + if data["player_two"].has("speed"): + player_two_speed = data["player_two"]["speed"] as int + + if data.has("obstacle"): + if data["obstacle"].has("length"): + obstacle_length = data["obstacle"]["length"] as int + if data["obstacle"].has("speed"): + obstacle_speed = data["obstacle"]["speed"] as int diff --git a/Battle Pong/Main.gd b/Battle Pong/Main.gd index 43e797548303d62fcb1520e0c74fc0dcacb69f4f..bae7c8c639c25b6b63248e0366150135b365b204 100644 --- a/Battle Pong/Main.gd +++ b/Battle Pong/Main.gd @@ -17,6 +17,8 @@ var action_player_two = InputEventAction.new() var next_step_player_two = false var action_trainer_two = "" + + var Ball = preload("Ball.tscn") var ball = Ball.instance() var score_player_one = 0 @@ -75,10 +77,16 @@ func _ready(): set_ball() $PlayerOne.start($StartPositionPlayerOne.position) $PlayerTwo.start($StartPositionPlayerTwo.position) + if $"/root/GameSettings".obstacles_enabled: + set_obstacle() + display_message() update_score() pause() +func set_obstacle(): + + $Obstacle.start($StartPositionObstacleTop.position) func _input(_event): if Input.is_key_pressed(KEY_SPACE): @@ -93,6 +101,7 @@ func play(): update_score() playing = true ball.set_playing(playing) + $Obstacle.set_playing(playing) $DisplayMessage.visible = false @@ -130,6 +139,8 @@ func handle_score_event(): display_message() playing = false score_event = false + $Obstacle.set_playing(playing) + func update_score(): @@ -173,6 +184,7 @@ func handle_game_end(): + func _on_PlayerTwo_hit(): ball.ball_hit_paddle($PlayerTwo.position, $PlayerTwo/CollisionShape2D.shape.extents, false) pass # Replace with function body. @@ -297,6 +309,8 @@ func _on_data(id): $PlayerOne.run(game_playtime_per_step) $PlayerTwo.run(game_playtime_per_step) ball.run(game_playtime_per_step) + if $"/root/GameSettings".obstacles_enabled: + $Obstacle.run(game_playtime_per_step) timeout() @@ -383,12 +397,14 @@ func pause(): ball.set_pause(true) $PlayerOne.set_pause(true) $PlayerTwo.set_pause(true) + $Obstacle.set_pause(true) func unpause(): #print("unpause") ball.set_pause(false) $PlayerOne.set_pause(false) $PlayerTwo.set_pause(false) + $Obstacle.set_pause(false) func _on_LocalTwoPlayerTimer_timeout(): @@ -396,5 +412,7 @@ func _on_LocalTwoPlayerTimer_timeout(): $PlayerOne.run(game_playtime_per_step) $PlayerTwo.run(game_playtime_per_step) ball.run(game_playtime_per_step) + if $"/root/GameSettings".obstacles_enabled: + $Obstacle.run(game_playtime_per_step) timeout() pass # Replace with function body. diff --git a/Battle Pong/Main.tscn b/Battle Pong/Main.tscn index 1684f90fc939ea2a276a80a92abd2534c0fa8794..ba94fc7ebdc43b7d7e146e0cd2d03113082be571 100644 --- a/Battle Pong/Main.tscn +++ b/Battle Pong/Main.tscn @@ -1,8 +1,10 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://Player.tscn" type="PackedScene" id=1] [ext_resource path="res://Main.gd" type="Script" id=2] [ext_resource path="res://Wall.tscn" type="PackedScene" id=3] +[ext_resource path="res://Obstacle.tscn" type="PackedScene" id=4] +[ext_resource path="res://Obstacle.gd" type="Script" id=5] [node name="Node2D" type="Node2D"] pause_mode = 2 @@ -85,6 +87,16 @@ position = Vector2( 0, 600 ) [node name="LocalTwoPlayerTimer" type="Timer" parent="."] wait_time = 0.01 + +[node name="Obstacle" parent="." instance=ExtResource( 4 )] +visible = false +script = ExtResource( 5 ) + +[node name="StartPositionObstacleTop" type="Position2D" parent="."] +position = Vector2( 512, 0 ) + +[node name="StartPositionObstacleBottom" type="Position2D" parent="."] +position = Vector2( 512, 600 ) [connection signal="hit" from="PlayerTwo" to="." method="_on_PlayerTwo_hit"] [connection signal="hit" from="PlayerOne" to="." method="_on_PlayerOne_hit"] [connection signal="timeout" from="LocalTwoPlayerTimer" to="." method="_on_LocalTwoPlayerTimer_timeout"] diff --git a/Battle Pong/MainMenu.gd b/Battle Pong/MainMenu.gd index 15e891560a79f9d48f6bbd6c76835d7be67f975b..b6d2fb0a6d1eece31734de820f38c19c1eca626b 100644 --- a/Battle Pong/MainMenu.gd +++ b/Battle Pong/MainMenu.gd @@ -28,11 +28,10 @@ func _on_but_two_re_with_images_pressed(): func _on_but_two_re_with_position_pressed(): - $"/root/GameSettings".rendering_enabled = $vbox_two_learner/HBoxContainer/cb_rendering_enabled.is_pressed() $"/root/GameSettings".learn_with_images = false $"/root/GameSettings".trainings_mode_enabled = false if not $"/root/GameSettings".rendering_enabled: - $vbox_two_learner.hide() + $HBoxContainer.hide() $lbl_game_running.show() get_viewport().set_clear_mode(Viewport.CLEAR_MODE_ONLY_NEXT_FRAME) # Wait until the frame has finished before getting the texture. @@ -43,11 +42,11 @@ func _on_but_two_re_with_position_pressed(): func _on_cb_rendering_enabled_pressed(): - $"/root/GameSettings".rendering_enabled = $vbox_two_learner/HBoxContainer/cb_rendering_enabled.is_pressed() - if not $"/root/GameSettings".rendering_enabled: - $vbox_two_learner.hide() - else: - $lbl_game_running.show() + $"/root/GameSettings".rendering_enabled = $HBoxContainer/vbox_two_learner/HBoxContainer/cb_rendering_enabled.is_pressed() + #if not $"/root/GameSettings".rendering_enabled: + # $vbox_two_learner.hide() + #else: + # $lbl_game_running.show() print($"/root/GameSettings".rendering_enabled) @@ -71,3 +70,9 @@ func _on_but_trainer_with_position_pressed(): func _on_but_settings_pressed(): get_tree().change_scene("res://SettingsWindow.tscn") + + +func _on_ckb_Obstackles_pressed(): + $"/root/GameSettings".obstacles_enabled = $HBoxContainer/VBoxContainer/ckb_Obstackles.pressed + print($"/root/GameSettings".obstacles_enabled) + pass # Replace with function body. diff --git a/Battle Pong/MainMenu.tscn b/Battle Pong/MainMenu.tscn index 3e316c4166b0da2f18552ea68b40dc80f74de779..b1c8eb75005b54a2fc540c30b580f0770084dee8 100644 --- a/Battle Pong/MainMenu.tscn +++ b/Battle Pong/MainMenu.tscn @@ -24,46 +24,53 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="vbox_two_learner" type="VBoxContainer" parent="."] +[node name="HBoxContainer" type="HBoxContainer" parent="."] anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 margin_left = -132.5 -margin_top = -92.0 +margin_top = -80.0 margin_right = 132.5 -margin_bottom = 92.0 +margin_bottom = 80.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="vbox_two_learner" type="VBoxContainer" parent="HBoxContainer"] +margin_right = 265.0 +margin_bottom = 160.0 __meta__ = { "_edit_use_anchors_": false } -[node name="but_local_two_player" type="Button" parent="vbox_two_learner"] +[node name="but_local_two_player" type="Button" parent="HBoxContainer/vbox_two_learner"] margin_right = 265.0 margin_bottom = 20.0 text = "Local 2 Player" -[node name="HSeparator2" type="HSeparator" parent="vbox_two_learner"] +[node name="HSeparator2" type="HSeparator" parent="HBoxContainer/vbox_two_learner"] margin_top = 24.0 margin_right = 265.0 margin_bottom = 28.0 -[node name="but_two_re_with_images" type="Button" parent="vbox_two_learner"] +[node name="but_two_re_with_images" type="Button" parent="HBoxContainer/vbox_two_learner"] margin_top = 32.0 margin_right = 265.0 margin_bottom = 52.0 text = "2 RE with Images" -[node name="HBoxContainer" type="HBoxContainer" parent="vbox_two_learner"] +[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/vbox_two_learner"] margin_top = 56.0 margin_right = 265.0 margin_bottom = 80.0 -[node name="but_two_re_with_position" type="Button" parent="vbox_two_learner/HBoxContainer"] +[node name="but_two_re_with_position" type="Button" parent="HBoxContainer/vbox_two_learner/HBoxContainer"] margin_right = 126.0 margin_bottom = 24.0 text = "2 RE with Position" -[node name="cb_rendering_enabled" type="CheckBox" parent="vbox_two_learner/HBoxContainer"] +[node name="cb_rendering_enabled" type="CheckBox" parent="HBoxContainer/vbox_two_learner/HBoxContainer"] margin_left = 130.0 margin_right = 265.0 margin_bottom = 24.0 @@ -72,29 +79,50 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="HSeparator" type="HSeparator" parent="vbox_two_learner"] +[node name="HSeparator" type="HSeparator" parent="HBoxContainer/vbox_two_learner"] margin_top = 84.0 margin_right = 265.0 margin_bottom = 88.0 -[node name="but_trainer_with_images" type="Button" parent="vbox_two_learner"] +[node name="but_trainer_with_images" type="Button" parent="HBoxContainer/vbox_two_learner"] margin_top = 92.0 margin_right = 265.0 margin_bottom = 112.0 text = "RE Trainer with Images" -[node name="but_trainer_with_position" type="Button" parent="vbox_two_learner"] +[node name="but_trainer_with_position" type="Button" parent="HBoxContainer/vbox_two_learner"] margin_top = 116.0 margin_right = 265.0 margin_bottom = 136.0 text = "RE Trainer with Position" -[node name="but_settings" type="Button" parent="vbox_two_learner"] +[node name="but_settings" type="Button" parent="HBoxContainer/vbox_two_learner"] margin_top = 140.0 margin_right = 265.0 margin_bottom = 160.0 text = "Settings" +[node name="VSeparator" type="VSeparator" parent="HBoxContainer"] +margin_left = 269.0 +margin_right = 273.0 +margin_bottom = 160.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"] +margin_left = 277.0 +margin_right = 421.0 +margin_bottom = 160.0 + +[node name="lbl_GameModifier" type="Label" parent="HBoxContainer/VBoxContainer"] +margin_right = 144.0 +margin_bottom = 14.0 +text = "Game Modifier" + +[node name="ckb_Obstackles" type="CheckButton" parent="HBoxContainer/VBoxContainer"] +margin_top = 18.0 +margin_right = 144.0 +margin_bottom = 58.0 +text = "Obstacles" + [node name="lbl_game_running" type="Label" parent="."] visible = false anchor_left = 0.5 @@ -107,10 +135,11 @@ text = "Game runs without rendering" __meta__ = { "_edit_use_anchors_": false } -[connection signal="pressed" from="vbox_two_learner/but_local_two_player" to="." method="_on_but_local_two_player_pressed"] -[connection signal="pressed" from="vbox_two_learner/but_two_re_with_images" to="." method="_on_but_two_re_with_images_pressed"] -[connection signal="pressed" from="vbox_two_learner/HBoxContainer/but_two_re_with_position" to="." method="_on_but_two_re_with_position_pressed"] -[connection signal="pressed" from="vbox_two_learner/HBoxContainer/cb_rendering_enabled" to="." method="_on_cb_rendering_enabled_pressed"] -[connection signal="pressed" from="vbox_two_learner/but_trainer_with_images" to="." method="_on_but_trainer_with_images_pressed"] -[connection signal="pressed" from="vbox_two_learner/but_trainer_with_position" to="." method="_on_but_trainer_with_position_pressed"] -[connection signal="pressed" from="vbox_two_learner/but_settings" to="." method="_on_but_settings_pressed"] +[connection signal="pressed" from="HBoxContainer/vbox_two_learner/but_local_two_player" to="." method="_on_but_local_two_player_pressed"] +[connection signal="pressed" from="HBoxContainer/vbox_two_learner/but_two_re_with_images" to="." method="_on_but_two_re_with_images_pressed"] +[connection signal="pressed" from="HBoxContainer/vbox_two_learner/HBoxContainer/but_two_re_with_position" to="." method="_on_but_two_re_with_position_pressed"] +[connection signal="pressed" from="HBoxContainer/vbox_two_learner/HBoxContainer/cb_rendering_enabled" to="." method="_on_cb_rendering_enabled_pressed"] +[connection signal="pressed" from="HBoxContainer/vbox_two_learner/but_trainer_with_images" to="." method="_on_but_trainer_with_images_pressed"] +[connection signal="pressed" from="HBoxContainer/vbox_two_learner/but_trainer_with_position" to="." method="_on_but_trainer_with_position_pressed"] +[connection signal="pressed" from="HBoxContainer/vbox_two_learner/but_settings" to="." method="_on_but_settings_pressed"] +[connection signal="pressed" from="HBoxContainer/VBoxContainer/ckb_Obstackles" to="." method="_on_ckb_Obstackles_pressed"] diff --git a/Battle Pong/Obstacle.gd b/Battle Pong/Obstacle.gd new file mode 100644 index 0000000000000000000000000000000000000000..9e74b9bba33ef8654489d4927dcd9925077db319 --- /dev/null +++ b/Battle Pong/Obstacle.gd @@ -0,0 +1,57 @@ +extends StaticBody2D + +var speed=0 +var start_position +var playing = false +var is_paused = true +var obstacle_size +var top_position +var bottom_position +# Called when the node enters the scene tree for the first time. +func _ready(): + randomize() + #speed = rand_range(min_speed, max_speed) + speed = $"/root/GameSettings".obstacle_speed + + obstacle_size =$CollisionShape2D.shape.extents + top_position = 0- obstacle_size.y + bottom_position = $"/root/GameSettings".display_window_height + obstacle_size.y + pass # Replace with function body. + + +func run(delta): + if playing and not is_paused: + self.rotation = 0 + #self.linear_velocity = Vector2(dx, dy) * delta * speed + #var move = velocity.normalized() * delta * speed + #print(delta) + #var collision = self.move(move, false) + + + var velocity = Vector2() # The player's movement vector. + # Hier kommt die Erweiterung hin um Obstacles von oben oder unten kommen zu lassen + #if true: + velocity.y += 1 + # else: + # velocity.y -= 1 + + if velocity.length() > 0: + velocity = velocity.normalized() * speed + + position += velocity * delta + if position.y >bottom_position: + position = start_position + +func start(pos): + start_position = pos + start_position.y = top_position + position = start_position + show() + +func set_playing(_playing): + playing = _playing + +func set_pause(value): + get_tree().paused=value + is_paused = value + diff --git a/Battle Pong/Obstacle.tscn b/Battle Pong/Obstacle.tscn index 0db91a4ab8adb97cd5fc925662b894427c8106f4..690bd3d67d21484e2f64ad5be0d92a028367aa8d 100644 --- a/Battle Pong/Obstacle.tscn +++ b/Battle Pong/Obstacle.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=2 format=2] [sub_resource type="RectangleShape2D" id=1] -extents = Vector2( 19.9777, 20.0466 ) +extents = Vector2( 12.0822, 49.3651 ) [node name="Obstacle" type="StaticBody2D"] pause_mode = 1 @@ -12,10 +12,10 @@ collision_mask = 8 shape = SubResource( 1 ) [node name="ColorRect" type="ColorRect" parent="."] -margin_left = -20.0 -margin_top = -20.0 -margin_right = 20.0 -margin_bottom = 20.0 +margin_left = -12.0 +margin_top = -49.0 +margin_right = 12.0 +margin_bottom = 49.0 color = Color( 0, 0.0862745, 1, 1 ) __meta__ = { "_edit_use_anchors_": false diff --git a/Battle Pong/SettingsWindow.gd b/Battle Pong/SettingsWindow.gd index cfebdf653e89c9b49ad22c1f9d37b17147c5c0a4..920363404a89e942e2b76932b622887464f1b8d4 100644 --- a/Battle Pong/SettingsWindow.gd +++ b/Battle Pong/SettingsWindow.gd @@ -43,6 +43,9 @@ func update_gui(): $VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings/input_player_two_length.text = str($"/root/GameSettings".player_two_length) $VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings/input_player_two_speed.text = str($"/root/GameSettings".player_two_speed) + $VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings/input_obstacle_length.text = str($"/root/GameSettings".obstacle_length) + $VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings/input_obstacle_speed.text = str($"/root/GameSettings".obstacle_speed) + func _on_input_trainer_position_pressed(): if $VBoxContainer/grid_settings/vbox_trainer_settings/grid_trainer_settings/input_trainer_position.is_pressed(): @@ -77,6 +80,9 @@ func _on_but_save_pressed(): $"/root/GameSettings".player_two_length = $VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings/input_player_two_length.text as int $"/root/GameSettings".player_two_speed = $VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings/input_player_two_speed.text as int + + $"/root/GameSettings".obstacle_length = $VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings/input_obstacle_length.text as int + $"/root/GameSettings".obstacle_speed = $VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings/input_obstacle_speed.text as int $"/root/GameSettings".save_data() diff --git a/Battle Pong/SettingsWindow.tscn b/Battle Pong/SettingsWindow.tscn index 51b5e09d3952e41d3bc2b3854d5c444fb2d0f7a2..55cd2bdc5a6efc1bc45ae11c611d80a26b91e189 100644 --- a/Battle Pong/SettingsWindow.tscn +++ b/Battle Pong/SettingsWindow.tscn @@ -28,17 +28,17 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -margin_left = -141.5 -margin_top = -181.0 -margin_right = 141.5 -margin_bottom = 181.0 +margin_left = -173.5 +margin_top = -206.0 +margin_right = 173.5 +margin_bottom = 206.0 __meta__ = { "_edit_use_anchors_": false } [node name="grid_settings" type="GridContainer" parent="VBoxContainer"] margin_right = 347.0 -margin_bottom = 278.0 +margin_bottom = 332.0 columns = 3 __meta__ = { "_edit_use_anchors_": false @@ -421,27 +421,90 @@ margin_left = 43.0 margin_right = 101.0 margin_bottom = 24.0 -[node name="VSeparator5" type="HSeparator" parent="VBoxContainer"] +[node name="HSeparator8" type="HSeparator" parent="VBoxContainer/grid_settings"] +margin_top = 282.0 +margin_right = 163.0 +margin_bottom = 286.0 + +[node name="VSeparator5" type="HSeparator" parent="VBoxContainer/grid_settings"] +margin_left = 167.0 +margin_top = 282.0 +margin_right = 171.0 +margin_bottom = 286.0 + +[node name="HSeparator3" type="HSeparator" parent="VBoxContainer/grid_settings"] +margin_left = 175.0 margin_top = 282.0 margin_right = 347.0 margin_bottom = 286.0 -[node name="but_save" type="Button" parent="VBoxContainer"] +[node name="vbox_obstacle_settings" type="VBoxContainer" parent="VBoxContainer/grid_settings"] margin_top = 290.0 +margin_right = 163.0 +margin_bottom = 332.0 + +[node name="lbl_title_obstacle_settings" type="Label" parent="VBoxContainer/grid_settings/vbox_obstacle_settings"] +margin_right = 163.0 +margin_bottom = 14.0 +text = "Obstacle:" + +[node name="grid_obstacle_settings" type="GridContainer" parent="VBoxContainer/grid_settings/vbox_obstacle_settings"] +margin_top = 18.0 +margin_right = 163.0 +margin_bottom = 42.0 +columns = 2 + +[node name="lbl_obstacle_length" type="Label" parent="VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings"] +visible = false +margin_right = 58.0 +margin_bottom = 14.0 +text = "Length" + +[node name="input_obstacle_length" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings"] +visible = false +margin_left = 47.0 +margin_right = 105.0 +margin_bottom = 24.0 +editable = false + +[node name="lbl_obstacle_speed" type="Label" parent="VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings"] +margin_top = 5.0 +margin_right = 39.0 +margin_bottom = 19.0 +text = "Speed" + +[node name="input_obstacle_speed" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_obstacle_settings/grid_obstacle_settings"] +margin_left = 43.0 +margin_right = 101.0 +margin_bottom = 24.0 + +[node name="VSeparator6" type="VSeparator" parent="VBoxContainer/grid_settings"] +margin_left = 167.0 +margin_top = 290.0 +margin_right = 171.0 +margin_bottom = 332.0 + +[node name="VSeparator7" type="HSeparator" parent="VBoxContainer"] +margin_top = 336.0 +margin_right = 347.0 +margin_bottom = 340.0 + +[node name="but_save" type="Button" parent="VBoxContainer"] +margin_top = 344.0 margin_right = 347.0 -margin_bottom = 310.0 +margin_bottom = 364.0 text = "Save" [node name="but_cancel" type="Button" parent="VBoxContainer"] -margin_top = 314.0 +margin_top = 368.0 margin_right = 347.0 -margin_bottom = 334.0 +margin_bottom = 388.0 text = "Cancel" [node name="but_reset" type="Button" parent="VBoxContainer"] -margin_top = 338.0 +margin_top = 392.0 margin_right = 347.0 -margin_bottom = 358.0 +margin_bottom = 412.0 text = "Reset" [connection signal="pressed" from="VBoxContainer/grid_settings/vbox_trainer_settings/grid_trainer_settings/input_trainer_position" to="." method="_on_input_trainer_position_pressed"] [connection signal="pressed" from="VBoxContainer/but_save" to="." method="_on_but_save_pressed"] diff --git a/BattlePongAI/main_player_two_training.py b/BattlePongAI/main_player_two_training.py index 700d3e5a29e067ef54e64a83a6c67b759fc9dca0..f8cb045cca8a239d54a65b81122bd8aa2fbc8701 100644 --- a/BattlePongAI/main_player_two_training.py +++ b/BattlePongAI/main_player_two_training.py @@ -7,7 +7,7 @@ from Gym import Gym async def main(): print("Hello World!") - gym = Gym("localhost", 9080, "player_one") + gym = Gym("localhost", 9080, "player_two") recieved_values = await gym.connect_player() data = recieved_values[0] diff --git a/README.md b/README.md index 5a98972e4275322dd3c7cb29e1b859653967a0f6..727d7813e6fb2a5b16e704301d03e3e7f18da3c7 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,173 @@ # Battle Pong -Projekt für Vertiefung Simulation - Battle Pong baut auf dem klassischen Zwei-Spieler Pong auf.\ Allerdings soll es möglich sein die menschlichen Spieler durch Reinforcement Algorithmen zu ersetzen. Zudem sollen die Algorithmen von den menschlichen Spielern lernen können. -Die Datenübertragung per TCP basiert auf REST. +Die Datenübertragung wird per Websockets realisiert und die Daten sind in JSON kodiert. +Die Schnittstelle in Python ist an der [Gym API](https://gym.openai.com/) orientiert, um den Einstieg und die Nutzung möglichst einfach zu gestalten. + + +## Lernvarianten + + +- **learn with position**\ + Es werden die aktuelle Position von Spielern und Ball ausgegeben, sowie der Bewegungsvektor des Balls. + +- **learn with images**\ + Nach jedem Spielschritt wird das angezeigt Bild aufgenommen, verkleinert und entweder in RGB8 oder S/W umgewandelt, bevor es ausgegeben wird. + +- **Training**\ + In diesem Modus wird einer der Spieler (es können auch beide gleichzeitig trainiert werden) über die Tastatur gesteuert und die Eingabe während der Spielschritte zuzüglich Bild / Positionsangaben übertragen + + +# Installationsanleitung + +## Godot + +1. Repository als zip Downloaden + * Master Branch für den aktuellsten Stand + * Release, für stabile und getestete Version +2. Archiv entpacken und in das gewünschte Projektverzeichnis kopieren +3. Download der aktuellen [Godot Engine](https://godotengine.org/) (Windows - Standard version, 64-Bit) +4. Entpacken des zip-Archivs +5. Ausführen der Anwendung +6. Im Projekt Manager auf **Import** klicken + * "Project Path:" => "Entpacktes battle-pong Projektverzeichnis/Battle Pong/project.godot" auswählen +7. **Import & Edit** klicken + +## Python + +1. Ordner BattlePongAI in beliebiger IDE öffnen +2. imports installieren + +## Test + +1. Battle-Pong in Godot starten +2. Haken bei **Enable rendering** setzen +3. **2 RE with Position** klicken +4. BattlePongAI main.py starten +5. BattlePongAI main_player_two.py starten\ +=> Das Spiel wird gestartet und die beiden RoboCats (Default-Agents in Python) spielen gegeneinander.\ + Dadurch, dass das Rendern eingeschaltet wurde, kann das Spiel beobachtet werden. + + + +# Nutzungsanleitung + +## Godot + +### **Spiel Starten** +Sobald das Projekt im Godot Projektmanager imortiert und gestartet wurde, kann das Spiel mit einem Klick auf den **Play Button** oder Drücken der **F5 Taste** gestartet werden. Zu Beginn wird das Hauptmenü angezeigt, in welchem der Spielmodus ausgewählt wird und das **Settings Window** aufgerufen werden kann, wo verschiedene Einstellungen für das Spiel hinterlegt sind. + +### Spielmodi + +* **Local 2 Player**\ +Spieler 1 und Spieler 2 werden über die Tastatur gesteuert. Der Websocket-Server wird nicht gestartet. + * Spieler 1:\ + Hoch => **W Taste**\ + Runter => **S Taste** + * Spieler 2:\ + Hoch => **Pfeiltaste Hoch**\ + Runter => **Pfeiltaste Runter** +* **2 RE with Images**\ +Nach jedem Spielschritt wird ein "Screenshot" des Spiels erstellt, verkleinert, konvertiert (RGB8/SW8) und mit an die Agenten übertragen. +* **2 RE with Position**\ +Es werden die Positionen der Spieler, des Balls, sowie der Bewegungsvektor des Balls an die Agenten übertragen + * Enable rendering\ + Ist standartmäßig ausgeschaltet, da das Rendern für diesen Modus nicht erforderlich ist und das Spiel ohne Rendern **erheblich schneller** läuft! +* **RE Trainer**\ +Wenn ein Agent im Trainingsmodus ist wird der ensprechende Spieler über die Tastatur gesteuert und dem Agenten wird diese Eingabe anschließend geschickt. Dadurch kann der aktuellen Situation eine "sinnvolle" Reaktion/Step zugeordnet werden, woraus der Agent dann lernen kann. + +* **Game Modifier**\ +Optionen mit denen das Spiel verändert werden kann, um neue Herausvorderungen zu schaffen. + * Obstacles:\ + ON => Ein vertikaler Block bewegt sich in der Mitte von Oben nach Unten. Sobald Er den den unteren Bildschrimrand verlassen hat beginnt Er wieder oben. + +### **Spieleinstellungen** +In dem **Settings Window** können allgemeine Einstellungen, Geschwindigkeit und Bildausgabe angepasst werden +* Game: + * Playtime/Step (Seconds):\ + Dauer eines Spielschrittes.\ + Sinnvoll sind Werte zwischen 0,1 - 0,001 Sekunden. + * Wins till end:\ + Siege die benötigt werden, bis das Spiel zurückgesetzt wird. + * Port:\ + Portangabe, über den der Websocket-Server angesprochen werden kann +* Image: (Spielmodus **Learn with Images**) + * Output RGB8:\ + Off => Bild wird in SW8 konvertiert (farbig)\ + On => Bild wird in RGB8 konvertiert (Graustufen)\ + Details unter [Godot Image Convert Funktion](https://docs.godotengine.org/en/stable/classes/class_image.html?highlight=rgb8#class-image-method-convert) und deren [Formate](https://docs.godotengine.org/en/stable/classes/class_image.html?highlight=rgb8#enum-image-format) + * Height:\ + Höhe des übertragenen Bildes in Pixel + * Width:\ + Breite des übertragenen Bildes in Pixel +* Trainer: (Spielmodus **Trainer**) + * Use realtime:\ + ON => Es wird bei jedem Spielschritt die Anzahl an Sekunden gewartet, welche unter **Playtime/Step** eingestellt ist. Die tatsächliche Spielzeit entspricht der realen Zeit.\ + OFF => Sobald beide beide Spieler per Websocket ihre Befehle erhalten haben wird der Spielschritt ausgeführt und das Ergebnis zurückgesendet.\ + **Achtung:** Es kann je nach eingestellter Schrittzeit zu einer enormen Geschwindigkeitserhöhung führen, wenn realtime ausgeschaltet ist, da die Spielzeit von der ingame Zeit entkoppelt ist und die tatsächliche Dauer eines Spielschritts nur noch von der Kommunikation und internen Berechnungen abhängt. +* Ball: + * Speed Min:\ + Geschwindigkeitsfaktor, mit dem der Ball zu Rundenbeginn beschleunigt wird + * Speed Max:\ + Maximaler Geschwindigkeitsfactor + * Speed Increment:\ + Konstante um die der aktuelle Geschwindigkeitsfaktor des Balls erhöht wird, sobald Dieser einen Spieler berührt. +* Player 1: + * Speed:\ + Geschwindigkeitsfaktor von Spieler 1 (links) +* Player 2: + * Speed:\ + Geschwindigkeitsfaktor von Spieler 2 (rechts) +* **Save**\ +Speichert die Einstellungen in data.json und wechselt zum Hauptmenü +* **Cancel**\ +Verwirft nicht gespeicherte Änderungen und geht zurück zum Hauptmenü +* **Reset**\ +Lädt die im Code hinterlegten Standartwerte, speichert Sie jedoch nicht + + + +## Python +Der Pythoncode enthält die Client-Seite der API (gym.py) welche die Kommunikation mit Godot regelt. Dabei steht ein Enum mit den möglichen Schritten bereit und es werden die übertragenen Daten interpretiert. + +### Klassen main / main_player_two +In beiden Klassen sind default Agenten implementiert (RoboCat) welche versuchen die aktuelle Höhe des Balls zu halten.\ +"main.py" steuert den Spieler 1 und "main_player_two.py" steuert Spieler 2. \ +"main.py" initialisiert das Spiel und beide Spieler laden sich zu beginn die "Start"-Observation. Anschließend versuchen beide Spieler die Höhe des Balls zu halten.\ +In "main.py" ist zudem beispielhaft implementiert wie das übertragene Bild geladen wird. Um es direkt anzeigen zulassen muss in der Funktion show_image() die Zeile *image.show()* einkommentiert werden. + +### Klasse Gym +Die gesamte Kommunikation ist in der Klassse "Gym" abgebildet und gekapselt. Bei jeder Kommunikation wird die aktuelle Observation zurückgegeben.\ +Die für die Nutzung interessanten Funktionen sind: +* *gym.connect_player()*\ +Meldet den Agenten in Godot an und erhält die initiale Oberservation. +* *gym.reset()* \ +Startet das Spiel. +* *gym.step()*\ +Sendet die Aktion, welche im nächsten Step ausgeführt werden soll. Diese muss vom Typ *ActionSpace* sein. + +### show_images() +In der Datei "show_image.py" ist die Funktion *show_image()* implementiert, welche das übertragene Bild zuerst von Base64 zurückkonvertiert, anschließend ein Bild aus dem Bytearray erstellt und dieses zurück gibt. -# Ziele +## Datenübertragung +In Godot läuft ein Websocket-Server und in Python sind die Websocket-Clients realisiert.\ +Die Python Agenten senden Steuerungsbefehle an das Spiel und erhalten Informationen zurück. -- Manuelle Steuerung per Tastatur -- Steuerung durch Reinforcement Algorithmen (vgl. Gym Open AI) per TCP -- Headless Server Modus, in dem das Rendering deaktiviert ist -- Ist ein Algorithmus per TCP verbunden und ein Mensch spielt, dann wird dennoch die Obervation gesendet +### Befehl +In dem Befehl steht die Spielerposition (player_one / player_two) und der nächste Step. +Mit Hilfe der Spielerposition kann die (Websocket) Client ID dem entsprechenden Spieler zugeordnet werden, um die auf den jeweiligen Spieler zugeschittene Antwort richtig zu versenden. +Die Stepanweisung gibt an wie sich der Spieler im kommenden Step verhalten möchte. Damit die Befehle in Godot dem einzelnen Spieler zugeordent werden können haben die Stepanweisungen das Prefix "player_one_" oder "player_two_". -# Optionale Ziele +### Antwort +Die Antwort wird im JSON Format geschickt, was einen einfachen Zusammenbau und Interpretation ermöglicht. Zudem kann die Antwort dynamisch erweitert werden. -- Verschiedene Modi, die über eine Settings-Datei eingestellt werden können -- Speichern der Einstellungen in einer Datei -- Anpassen der Einstellungen durch ein In-Game Menü - - z.B. : Tastaturbelegung, Ports, ... +* Observation:\ +Enhält informationen zu den einzelnen Spielern (Position) und dem Ball (Position, Bewegungsvektor) +* Reward:\ +Aktuelle Rewards beider Spieler +* Informationen:\ +Punktezahl (Score) beider Spieler und im Modus **Train with Images** den Screenshot +* Spielstatus:\ +Boolscher Wert => Läuft das Spiel noch? \ No newline at end of file