Browse Source

Added a pong multiplayer example

Juan Linietsky 9 years ago
parent
commit
00f5f054ad

+ 74 - 0
networking/pong_multiplayer/ball.gd

@@ -0,0 +1,74 @@
+
+extends Area2D
+
+const DEFAULT_SPEED=80
+
+var direction = Vector2(1,0)
+var ball_speed = DEFAULT_SPEED
+var stopped=false
+
+
+
+onready var screen_size = get_viewport_rect().size
+
+sync func _reset_ball(for_left):
+	
+	set_pos( screen_size /2 )
+	if (for_left):
+		direction = Vector2(-1,0)
+	else:
+		direction = Vector2( 1,0)
+
+	ball_speed = DEFAULT_SPEED
+
+sync func stop():
+	stopped=true
+
+func _process(delta):
+	
+	# ball will move normally for both players
+	# even if it's sightly out of sync between them
+	# so each player sees the motion as smooth and not jerky
+	
+	if (not stopped):
+		translate( direction * ball_speed * delta ) 
+	
+	# check screen bounds to make ball bounce
+	
+	var ball_pos = get_pos()
+	if ((ball_pos.y < 0 and direction.y < 0) or (ball_pos.y > screen_size.y and direction.y > 0)):
+		direction.y = -direction.y
+		
+	if (is_network_master()):
+		# only master will decide when the ball is out in the left side (it's own side)
+		# this makes the game playable even if latency is high and ball is going fast
+		# otherwise ball might be out in the other player's screen but not this one
+		
+		if (ball_pos.x < 0 ):
+			get_parent().rpc("update_score",false)
+			rpc("_reset_ball",false)
+	else:
+		# only the slave will decide when the ball is out in the right side (it's own side)
+		# this makes the game playable even if latency is high and ball is going fast
+		# otherwise ball might be out in the other player's screen but not this one
+		
+		if (ball_pos.x > screen_size.x):
+			get_parent().rpc("update_score",true)
+			rpc("_reset_ball",true)
+		
+	
+sync func bounce(left,random):
+	
+	#using sync because both players can make it bounce
+	if (left):		
+		direction.x = abs(direction.x)
+	else:
+		direction.x = -abs(direction.x)
+		
+	ball_speed *= 1.1
+	direction.y = random*2.0 - 1
+	direction = direction.normalized()
+
+func _ready():
+	set_process(true)
+

BIN
networking/pong_multiplayer/ball.png


+ 33 - 0
networking/pong_multiplayer/ball.tscn

@@ -0,0 +1,33 @@
+[gd_scene load_steps=4 format=1]
+
+[ext_resource path="res://ball.gd" type="Script" id=1]
+[ext_resource path="res://ball.png" type="Texture" id=2]
+
+[sub_resource type="CircleShape2D" id=1]
+
+custom_solver_bias = 0.0
+radius = 5.11969
+
+[node name="ball" type="Area2D"]
+
+input/pickable = true
+shapes/0/shape = SubResource( 1 )
+shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 )
+shapes/0/trigger = false
+gravity_vec = Vector2( 0, 1 )
+gravity = 98.0
+linear_damp = 0.1
+angular_damp = 1.0
+script/script = ExtResource( 1 )
+
+[node name="sprite" type="Sprite" parent="."]
+
+texture = ExtResource( 2 )
+
+[node name="shape" type="CollisionShape2D" parent="."]
+
+shape = SubResource( 1 )
+trigger = false
+_update_shape_index = 0
+
+

+ 20 - 0
networking/pong_multiplayer/engine.cfg

@@ -0,0 +1,20 @@
+[application]
+
+name="Pong Multiplayer"
+main_scene="res://lobby.tscn"
+icon="res://icon.png"
+
+[display]
+
+width=640
+height=400
+stretch_2d=true
+
+[input]
+
+move_up=[key(Up)]
+move_down=[key(Down)]
+
+[render]
+
+default_clear_color=#ff000000

BIN
networking/pong_multiplayer/icon.png


+ 104 - 0
networking/pong_multiplayer/lobby.gd

@@ -0,0 +1,104 @@
+
+extends Control
+
+const DEFAULT_PORT = 8910 # some random number, pick your port properly
+
+#### Network callbacks from SceneTree ####
+
+# callback from SceneTree
+func _player_connected(id):
+	#someone connected, start the game!
+	var pong = load("res://pong.tscn").instance()
+	pong.connect("game_finished",self,"_end_game",[],CONNECT_DEFERRED) # connect deferred so we can safely erase it from the callback
+	
+	get_tree().get_root().add_child(pong)
+	hide()
+
+func _player_disconnected(id):
+
+	if (get_tree().is_network_server()):
+		_end_game("Client disconnected")
+	else:
+		_end_game("Server disconnected")
+
+# callback from SceneTree, only for clients (not server)
+func _connected_ok():
+	# will not use this one
+	pass
+	
+# callback from SceneTree, only for clients (not server)	
+func _connected_fail():
+
+	_set_status("Couldn't connect",false)
+	
+	get_tree().set_network_peer(null) #remove peer
+	
+	get_node("panel/join").set_disabled(false)
+	get_node("panel/host").set_disabled(false)
+
+func _server_disconnected():
+	_end_game("Server disconnected")
+	
+##### Game creation functions ######
+
+func _end_game(with_error=""):
+	if (has_node("/root/pong")):
+		#erase pong scene
+		get_node("/root/pong").free() # erase immediately, otherwise network might show errors (this is why we connected deferred above)
+		show()
+	
+	get_tree().set_network_peer(null) #remove peer
+	
+	get_node("panel/join").set_disabled(false)
+	get_node("panel/host").set_disabled(false)
+	
+	_set_status(with_error,false)
+
+func _set_status(text,isok):
+	#simple way to show status		
+	if (isok):
+		get_node("panel/status_ok").set_text(text)
+		get_node("panel/status_fail").set_text("")
+	else:
+		get_node("panel/status_ok").set_text("")
+		get_node("panel/status_fail").set_text(text)
+
+func _on_host_pressed():
+	
+	var host = NetworkedMultiplayerENet.new()
+	var err = host.create_server(DEFAULT_PORT,1) # max: 1 peer, since it's a 2 players game
+	if (err!=OK):
+		#is another server running?
+		_set_status("Can't host, address in use.",false)
+		return
+		
+	get_tree().set_network_peer(host)
+	get_node("panel/join").set_disabled(true)
+	get_node("panel/host").set_disabled(true)
+	_set_status("Waiting for player..",true)
+
+func _on_join_pressed():
+	
+	var ip = get_node("panel/address").get_text()
+	if (not ip.is_valid_ip_address()):
+		_set_status("IP address is invalid",false)
+		return
+	
+	var host = NetworkedMultiplayerENet.new()
+	host.create_client(ip,DEFAULT_PORT)
+	get_tree().set_network_peer(host)
+	
+	_set_status("Connecting..",true)
+
+
+
+### INITIALIZER ####
+	
+func _ready():
+	# connect all the callbacks related to networking
+	get_tree().connect("network_peer_connected",self,"_player_connected")
+	get_tree().connect("network_peer_disconnected",self,"_player_disconnected")
+	get_tree().connect("connected_to_server",self,"_connected_ok")
+	get_tree().connect("connection_failed",self,"_connected_fail")
+	get_tree().connect("server_disconnected",self,"_server_disconnected")
+	

+ 144 - 0
networking/pong_multiplayer/lobby.tscn

@@ -0,0 +1,144 @@
+[gd_scene load_steps=2 format=1]
+
+[ext_resource path="res://lobby.gd" type="Script" id=1]
+
+[node name="lobby" type="Control"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 0.0
+margin/top = 0.0
+margin/right = 40.0
+margin/bottom = 40.0
+script/script = ExtResource( 1 )
+
+[node name="title" type="Label" parent="."]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 210.0
+margin/top = 40.0
+margin/right = 430.0
+margin/bottom = 80.0
+text = "Multiplayer Pong"
+align = 1
+valign = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="panel" type="Panel" parent="."]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 210.0
+margin/top = 160.0
+margin/right = 430.0
+margin/bottom = 270.0
+
+[node name="address_label" type="Label" parent="panel"]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 10.0
+margin/top = 10.0
+margin/right = 62.0
+margin/bottom = 24.0
+text = "Address"
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="address" type="LineEdit" parent="panel"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 10.0
+margin/top = 30.0
+margin/right = 210.0
+margin/bottom = 54.0
+text = "127.0.0.1"
+placeholder/alpha = 0.6
+focus_mode = 2
+caret/caret_blink = false
+caret/caret_blink_speed = 0.65
+
+[node name="host" type="Button" parent="panel"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 10.0
+margin/top = 60.0
+margin/right = 90.0
+margin/bottom = 80.0
+toggle_mode = false
+enabled_focus_mode = 2
+shortcut = null
+text = "Host"
+flat = false
+
+[node name="join" type="Button" parent="panel"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 130.0
+margin/top = 60.0
+margin/right = 210.0
+margin/bottom = 80.0
+toggle_mode = false
+enabled_focus_mode = 2
+shortcut = null
+text = "Join"
+flat = false
+
+[node name="status_ok" type="Label" parent="panel"]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 10.0
+margin/top = 90.0
+margin/right = 210.0
+margin/bottom = 104.0
+custom_colors/font_color = Color( 0, 1, 0.015625, 1 )
+align = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="status_fail" type="Label" parent="panel"]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 10.0
+margin/top = 90.0
+margin/right = 210.0
+margin/bottom = 104.0
+custom_colors/font_color = Color( 1, 0, 0, 1 )
+align = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[connection signal="pressed" from="panel/host" to="." method="_on_host_pressed"]
+
+[connection signal="pressed" from="panel/join" to="." method="_on_join_pressed"]
+
+

+ 69 - 0
networking/pong_multiplayer/paddle.gd

@@ -0,0 +1,69 @@
+extends Area2D
+
+export var left=false
+
+const MOTION_SPEED=150
+
+var motion = 0
+var you_hidden=false
+
+onready var screen_size = get_viewport_rect().size
+
+#synchronize position and speed to the other peers
+slave func set_pos_and_motion(p_pos,p_motion):
+	set_pos(p_pos)
+	motion=p_motion
+
+func _hide_you_label():
+	you_hidden=true
+	get_node("you").hide()
+
+func _process(delta):
+		
+	#is the master of the paddle		
+	if (is_network_master()):		
+	
+		motion = 0
+		if (Input.is_action_pressed("move_up")):
+			motion -= 1
+		elif (Input.is_action_pressed("move_down")):
+			motion += 1
+
+		if (not you_hidden and motion!=0):
+			_hide_you_label()
+
+									
+		motion*=MOTION_SPEED
+		
+		#using unreliable to make sure position is updated as fast as possible, even if one of the calls is dropped
+		rpc_unreliable("set_pos_and_motion",get_pos(),motion)
+		
+	else:
+		if (not you_hidden):
+			_hide_you_label()
+		
+	
+	translate( Vector2(0,motion*delta) )
+	
+	# set screen limits
+	
+	var pos = get_pos()
+	
+	if (pos.y < 0 ):
+		set_pos( Vector2( pos.x, 0) )
+	elif (pos.y > screen_size.y):	
+		set_pos( Vector2( pos.x, screen_size.y) )
+	
+	
+	
+func _ready():
+	set_process(true)
+
+func _on_paddle_area_enter( area ):
+	
+	if (is_network_master()):
+		area.rpc("bounce",left,randf()) #random for new direction generated on each peer
+
+	
+		
+		

BIN
networking/pong_multiplayer/paddle.png


+ 53 - 0
networking/pong_multiplayer/paddle.tscn

@@ -0,0 +1,53 @@
+[gd_scene load_steps=4 format=1]
+
+[ext_resource path="res://paddle.gd" type="Script" id=1]
+[ext_resource path="res://paddle.png" type="Texture" id=2]
+
+[sub_resource type="CapsuleShape2D" id=1]
+
+custom_solver_bias = 0.0
+radius = 4.78568
+height = 23.6064
+
+[node name="paddle" type="Area2D"]
+
+input/pickable = true
+shapes/0/shape = SubResource( 1 )
+shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 )
+shapes/0/trigger = false
+gravity_vec = Vector2( 0, 1 )
+gravity = 98.0
+linear_damp = 0.1
+angular_damp = 1.0
+script/script = ExtResource( 1 )
+left = false
+
+[node name="sprite" type="Sprite" parent="."]
+
+texture = ExtResource( 2 )
+
+[node name="shape" type="CollisionShape2D" parent="."]
+
+shape = SubResource( 1 )
+trigger = false
+_update_shape_index = 0
+
+[node name="you" type="Label" parent="."]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = -26.0
+margin/top = -33.0
+margin/right = 27.0
+margin/bottom = -19.0
+text = "You"
+align = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[connection signal="area_enter" from="." to="." method="_on_paddle_area_enter"]
+
+

+ 52 - 0
networking/pong_multiplayer/pong.gd

@@ -0,0 +1,52 @@
+
+extends Node2D
+
+const SCORE_TO_WIN=10
+
+var score_left = 0
+var score_right = 0
+
+signal game_finished()
+
+sync func update_score(add_to_left):
+	if (add_to_left):
+		
+		score_left+=1
+		get_node("score_left").set_text( str(score_left) )
+	else:
+		
+		score_right+=1
+		get_node("score_right").set_text( str(score_right) )
+		
+	var game_ended = false
+	
+	if (score_left==SCORE_TO_WIN):
+		get_node("winner_left").show()
+		game_ended=true
+	elif (score_right==SCORE_TO_WIN):
+		get_node("winner_right").show()
+		game_ended=true
+		
+	if (game_ended):
+		get_node("exit_game").show()
+		get_node("ball").rpc("stop")
+
+func _on_exit_game_pressed():
+	emit_signal("game_finished")	
+
+func _ready():
+	
+	# by default, all nodes in server inherit from master
+	# while all nodes in clients inherit from slave
+		
+	if (get_tree().is_network_server()):		
+		#set to not control player 2. since it's master as everything else
+		get_node("player2").set_network_mode(NETWORK_MODE_SLAVE)
+	else:
+		#set to control player 2, as it's slave as everything else
+		get_node("player2").set_network_mode(NETWORK_MODE_MASTER)
+	
+	#let each paddle know which one is left, too
+	get_node("player1").left=true
+	get_node("player2").left=false
+

+ 122 - 0
networking/pong_multiplayer/pong.tscn

@@ -0,0 +1,122 @@
+[gd_scene load_steps=5 format=1]
+
+[ext_resource path="res://pong.gd" type="Script" id=1]
+[ext_resource path="res://separator.png" type="Texture" id=2]
+[ext_resource path="res://paddle.tscn" type="PackedScene" id=3]
+[ext_resource path="res://ball.tscn" type="PackedScene" id=4]
+
+[node name="pong" type="Node2D"]
+
+script/script = ExtResource( 1 )
+
+[node name="separator" type="Sprite" parent="."]
+
+transform/pos = Vector2( 320, 200 )
+texture = ExtResource( 2 )
+
+[node name="player1" parent="." instance=ExtResource( 3 )]
+
+transform/pos = Vector2( 32.49, 188.622 )
+
+[node name="sprite" parent="player1"]
+
+modulate = Color( 1, 0, 0.960938, 1 )
+
+[node name="player2" parent="." instance=ExtResource( 3 )]
+
+transform/pos = Vector2( 608.88, 188.622 )
+
+[node name="sprite" parent="player2"]
+
+modulate = Color( 0, 0.929688, 1, 1 )
+
+[node name="ball" parent="." instance=ExtResource( 4 )]
+
+transform/pos = Vector2( 320.387, 189.525 )
+
+[node name="score_left" type="Label" parent="."]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 240.0
+margin/top = 10.0
+margin/right = 280.0
+margin/bottom = 30.0
+text = "0"
+align = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="score_right" type="Label" parent="."]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 360.0
+margin/top = 10.0
+margin/right = 400.0
+margin/bottom = 30.0
+text = "0"
+align = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="winner_left" type="Label" parent="."]
+
+visibility/visible = false
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 190.0
+margin/top = 170.0
+margin/right = 267.0
+margin/bottom = 184.0
+text = "The Winner!"
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="winner_right" type="Label" parent="."]
+
+visibility/visible = false
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 380.0
+margin/top = 170.0
+margin/right = 457.0
+margin/bottom = 184.0
+text = "The Winner!"
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="exit_game" type="Button" parent="."]
+
+visibility/visible = false
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 280.0
+margin/top = 340.0
+margin/right = 360.0
+margin/bottom = 360.0
+toggle_mode = false
+enabled_focus_mode = 2
+shortcut = null
+text = "Exit Game"
+flat = false
+
+[connection signal="pressed" from="exit_game" to="." method="_on_exit_game_pressed"]
+
+
+[editable path="player1"]
+[editable path="player2"]

BIN
networking/pong_multiplayer/separator.png