Nätverksspel med Godot - Från Tutorial till Egen Implementation 🌐
I detta projekt får ni testa på att skapa en nätverkad applikation, som också har server+klienter, i en helt annan kontext än specifikt webserverprogrammering.
Den största skillnaden är att här används RPC (Remote Procedure Calls) som tillåter klienter att anropa metoder på andra klienter (ganska likt hur en form action i SvelteKit anropar en Action på servern).
Detta gör ni för att bredda er kunskap om serverprogrammering, och för att avsluta år 3 med att anknyta igen till det ni lärde er i år 1.
Ditt uppdrag 🎯
Bemästra multiplayer game development genom att först följa en etablerad tutorial, sedan designa och implementera ditt eget nätverksspel:
- Förstå Godot's networking system genom hands-on tutorial
- Lära dig server-client arkitektur och RPC-system
- Planera och designa ditt eget multiplayer-koncept
- Implementera networking, synchronization och player management
- Hantera real-time gameplay och latency issues
Mål: Skapa ett fungerande multiplayer-spel som andra kan ansluta till och spela tillsammans!
Denna modul är uppdelad i två delar: först följer du en beprövad tutorial för att lära grunderna, sedan skapar du något helt eget!
Fas 1: Genomför grundläggande tutorial 📚
Steg 1: Navigera till introduktion för multiplayer i godot
Börja med den befintliga tutorial:
[/kurser/applied_programming/godot_multiplayer/](Följ denna länk!)
Innan du fortsätter med nästa fas måste du ha:
- ✅ Genomfört hela tutorial-delen
- ✅ Fått tutorial-exemplet att fungera lokalt
- ✅ Testat multiplayer-funktionaliteten
- ✅ Förstått grundläggande networking-koncepten
Steg 2: Dokumentera dina lärdomar från tutorial
Reflektera över tutorial-upplevelsen:
# Min Tutorial-genomgång
## Vad lärde jag mig?
-
-
-
## Vilka networking-koncept användes?
-
-
-
## Vilka utmaningar stötte jag på?
-
-
-
## Vad fungerade bra?
-
-
-
## Vad skulle jag vilja förbättra/ändra?
-
-
-
Steg 3: Analysera tutorial-koden
Förstå arkitekturen:
Din uppgift 1: Identifiera nyckelkomponenter
- Vilka scripts hanterar networking?
- Hur implementeras server/client-logik?
- Vilka RPC-anrop används och varför?
- Hur hanteras player spawn/despawn?
Din uppgift 2: Networking flow-analys
- Rita upp eller beskriv dataflödet när en spelare ansluter
- Vad händer när en spelare gör en action?
- Hur synkroniseras spelarpositioner?
- Vilka data skickas mellan klienter?
# Networking Flow (din analys)
1. Server start:
-
2. Client connection:
-
3. Player spawn:
-
4. Game action (t.ex. movement):
-
5. Data sync:
-
Fas 2: Planera ditt eget multiplayer-spel 🎮
Steg 1: Brainstorma spelkoncept
Vad vill du skapa?
Din uppgift 3: Välj spelgenre och tema
Välj EN av följande kategorier eller föreslå egen:
Casual Multiplayer:
- 🏓 Pong Battle - Multiplayer Pong med power-ups
- 🐍 Snake Arena - Flera snakes som tävlar om mat
- 🚗 Racing Chaos - Enkel racing med kollisioner
- ⚽ Mini Soccer - 2v2 fotboll med simpel fysik
Action/Combat:
- ⚔️ Arena Fighter - Top-down combat arena
- 🏹 Battle Royale Mini - Litet område, sista person vinner
- 🔫 Tank Wars - Klassisk tank-stridsplats
- 🥊 Duel Masters - 1v1 fighting game
Co-op Adventures:
- 🗝️ Dungeon Escape - Samarbeta för att fly labyrint
- 🧩 Puzzle Together - Lös pussel gemensamt
- 🌊 Raft Survival - Bygg och överlev tillsammans
- 👻 Ghost Hunt - En spelare är spöke, andra jagar
Strategy/Building:
- 🏰 Tower Defense Co-op - Försvara tillsammans
- 🎯 Resource Race - Samla resources snabbast
- 🏗️ Build Battle - Tävla om bästa byggnad
- 🌱 Farm Together - Multiplayer farming
Din uppgift 4: Definiera ditt spelkoncept
Fyll i för ditt valda spel:
# Mitt Multiplayer-spelkoncept
## Speltitel:
[Ditt spelnamn här]
## Genre:
[Casual/Action/Co-op/Strategy]
## Kort beskrivning:
[2-3 meningar om vad spelet går ut på]
## Antal spelare:
[2-4 spelare? 2v2? Free-for-all?]
## Huvudmekanik:
[Vad gör spelarna? Attackerar? Samlar? Bygger?]
## Vinstkondition:
[Hur vinner man? Första till X? Sista överlevande? Tidsbegränsat?]
## Unique selling point:
[Vad gör ditt spel annorlunda/kul?]
Steg 2: Analysera networking-behov
Vilka data behöver synkroniseras?
Din uppgift 5: Identifiera nätverksbehov
Baserat på ditt spelkoncept, analysera:
# Networking-analys för mitt spel
## Spelardata som måste synkroniseras:
- Position (x, y)
- Rotation/riktning
- [ ] Health/lives
- [ ] Score/points
- [ ] Animation state
- [ ] Inventory/equipment
- [ ] Special abilities cooldowns
- [ ] [Lägg till specifikt för ditt spel]
## Game state som måste delas:
- [ ] Match status (waiting/playing/ended)
- [ ] Timer/round counter
- [ ] Spawned objects (bullets, power-ups, etc.)
- [ ] Environmental changes
- [ ] [Lägg till specifikt för ditt spel]
## RPC-anrop jag behöver:
- [ ] Player actions (shoot, jump, use ability)
- [ ] Object spawning/destroying
- [ ] Game state changes
- [ ] Chat/communication
- [ ] [Lägg till specifikt för ditt spel]
## Networking-frekvens:
- Position updates: [60 FPS? 30 FPS? On change?]
- Game events: [Immediate? Batched?]
- UI updates: [Real-time? Periodic?]
Steg 3: Designa spelarkitektur
Vem ansvarar för vad?
Din uppgift 6: Authority och ansvar
# Authority-design för mitt spel
## Server/Host ansvarar för:
- [ ] Game state (timer, scores, round status)
- [ ] Collision detection och validation
- [ ] Spawning av shared objects
- [ ] Win/loss determination
- [ ] [Specifikt för ditt spel]
## Varje klient ansvarar för:
- [ ] Sin egen spelar-input
- [ ] Lokal movement prediction
- [ ] UI rendering
- [ ] Sound effects
- [ ] [Specifikt för ditt spel]
## Konfliktlösning:
- Vad händer om två spelare gör motsägelsefulla actions?
- Hur hanteras lag/latency?
- Vad händer vid disconnection?
Fas 3: Teknisk planering och setup 🛠️
Steg 1: Projektstruktur
Organisera ditt projekt:
Din uppgift 7: Skapa projektstruktur
MinMultiplayerGame/
├── scenes/
│ ├── Main.tscn # Huvudmeny och lobby
│ ├── GameWorld.tscn # Själva spelarenan
│ ├── Player.tscn # Spelarkaraktär
│ ├── UI/
│ │ ├── MainMenu.tscn # Start-meny
│ │ ├── Lobby.tscn # Vänterum före spel
│ │ └── GameUI.tscn # In-game interface
│ └── GameObjects/
│ ├── [DittSpecialObjekt].tscn
│ └── [AndraSpelObjekt].tscn
├── scripts/
│ ├── MultiplayerManager.gd # Baserat på tutorial
│ ├── GameManager.gd # Spellogik och state
│ ├── Player.gd # Spelarcontroller
│ ├── NetworkedObject.gd # Bas för synkade objekt
│ └── [DittSpelScript].gd
└── assets/
├── sprites/
├── sounds/
└── fonts/
Skapa denna struktur i Godot och börja med de grundläggande scenerna.
Steg 2: Anpassa tutorial-koden
Bygg vidare på tutorial-grunden:
Din uppgift 8: Modifiera MultiplayerManager
Baserat på tutorial-koden, anpassa för ditt spel:
# MultiplayerManager.gd - Din anpassning
extends Node
# Signals för ditt specifika spel
signal game_started
signal player_scored(player_id: int, score: int)
signal game_ended(winner_id: int)
# [Lägg till signals för ditt spel]
var game_state = {
"status": "waiting", # waiting, playing, ended
"players": {},
"scores": {},
# [Lägg till specifik game state för ditt spel]
}
# Din uppgift: Implementera game-specifika funktioner
func start_game():
# Vad ska hända när spelet startar?
pass
func end_game():
# Vad ska hända när spelet slutar?
pass
# RPC-funktioner för ditt spel
@rpc("any_peer", "call_local")
func player_action(action_type: String, data: Dictionary):
# Hantera spelar-actions (attack, jump, etc.)
pass
@rpc("authority", "call_local")
func update_game_state(new_state: Dictionary):
# Synkronisera game state
pass
Steg 3: Implementera Player-klassen
Spelarkaraktär för ditt spel:
Din uppgift 9: Utöka Player.gd från tutorial
Baserat på tutorial-koden, utöka din spelarkaraktär:
# Player.gd - Utöka från tutorial
extends CharacterBody2D
# Från tutorial (behåll dessa)
const SPEED = 200.0
const JUMP_VELOCITY = -300.0
var syncPos = Vector2(0,0)
@onready var animated_sprite_2d = $AnimatedSprite2D
@export var PlayerCamera : PackedScene
# Nya properties för ditt spel
@export var max_health: int = 100
@export var current_health: int = 100
@export var player_name: String = ""
@export var team_id: int = 0 # för team-baserade spel
# [Lägg till specifikt för ditt spel]
# Exempel för olika speltyper:
# @export var ammunition: int = 30 # För shooter
# @export var lives: int = 3 # För arcade games
# @export var score: int = 0 # För poängspel
# @export var power_level: int = 1 # För RPG-element
signal health_changed(new_health: int)
signal player_died(player_id: int)
func _ready():
# Behåll tutorial-logiken
if $MultiplayerSynchronizer.get_multiplayer_authority() == multiplayer.get_unique_id():
var camera = PlayerCamera.instantiate()
add_child(camera)
# Lägg till din egen setup
current_health = max_health
func _process(delta: float) -> void:
# Behåll tutorial movement (men bara för authority)
if $MultiplayerSynchronizer.get_multiplayer_authority() == multiplayer.get_unique_id():
handle_movement(delta)
handle_game_input() # Din nya input-hantering
else:
# Lerp position från tutorial
global_position = global_position.lerp(syncPos, delta * 10)
func handle_movement(delta: float):
# Tutorial movement code här
if not is_on_floor():
velocity += get_gravity() * delta
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
$AnimatedSprite2D.play("roll")
var direction := Input.get_axis("ui_left", "ui_right")
if direction > 0:
animated_sprite_2d.flip_h = false
elif direction < 0:
animated_sprite_2d.flip_h = true
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
syncPos = global_position
func handle_game_input():
# Din uppgift: Implementera spelspecifik input
# Exempel för olika speltyper:
# För shooter/combat:
if Input.is_action_just_pressed("shoot"):
perform_attack.rpc()
# För action games:
if Input.is_action_just_pressed("special_ability"):
use_special_ability.rpc()
# För puzzle/strategy:
if Input.is_action_just_pressed("interact"):
interact_with_object.rpc()
# RPC functions för ditt spel
@rpc("any_peer", "call_local")
func perform_attack():
# Din attack-logik här
print(player_name + " attacked!")
@rpc("any_peer", "call_local")
func take_damage(damage: int, from_player: int):
if $MultiplayerSynchronizer.get_multiplayer_authority() == multiplayer.get_unique_id():
current_health -= damage
health_changed.emit(current_health)
if current_health <= 0:
die()
@rpc("any_peer", "call_local")
func die():
player_died.emit($MultiplayerSynchronizer.get_multiplayer_authority())
# Hantera död (respawn, game over, etc.)
@rpc("any_peer", "call_local")
func use_special_ability():
# Din special ability här
pass
Steg 4: Utöka SceneManager
Hantera spellogik och player management:
Din uppgift 10: Utöka SceneManager.gd
# SceneManager.gd - Utöka från tutorial
extends Node2D
# Från tutorial
@export var PlayerScene : PackedScene
var index = 0
# Nya properties för ditt spel
var active_players: Dictionary = {}
var game_state = {
"status": "waiting", # waiting, playing, ended
"round_time": 0,
"scores": {}
}
signal game_started
signal game_ended
signal player_eliminated(player_id: int)
func _ready():
# Tutorial logik
for playerId in multiplayer.get_peers():
JoinPlayer(playerId)
# Anropa RPC för egen spelare
JoinPlayer.rpc(multiplayer.get_unique_id())
# Din nya logik
setup_game_rules()
func setup_game_rules():
# Din uppgift: Implementera spelspecifika regler
# Exempel:
# - Timer för rounds
# - Win conditions
# - Spawn points management
pass
@rpc("any_peer","call_local")
func JoinPlayer(playerId):
# Tutorial logik (behåll)
var player:Node = PlayerScene.instantiate()
var spawns : Array[Node] = get_tree().get_nodes_in_group("PlayerSpawnPoint")
var spawn : Node = spawns[index % len(spawns)]
var synchroniser = player.get_node("MultiplayerSynchronizer")
synchroniser.set_multiplayer_authority(playerId)
add_child(player)
player.global_position = spawn.global_position
index += 1
# Din nya logik
active_players[playerId] = player
player.player_died.connect(_on_player_died)
# Sätt spelnamn/team
player.player_name = "Player " + str(playerId)
check_start_conditions()
func check_start_conditions():
# Din uppgift: När ska spelet starta?
# Exempel: när alla spelare anslutit, efter countdown, etc.
if active_players.size() >= 2 and game_state.status == "waiting":
start_game()
@rpc("authority", "call_local")
func start_game():
game_state.status = "playing"
game_started.emit()
print("Game started!")
# Din spelspecifika start-logik
# Exempel: starta timer, spawna objekt, etc.
func _on_player_died(player_id: int):
# Din uppgift: Vad händer när en spelare dör?
# Exempel: eliminera från round, respawn, game over check
pass
@rpc("any_peer", "call_local")
func update_score(player_id: int, new_score: int):
game_state.scores[player_id] = new_score
check_win_conditions()
func check_win_conditions():
# Din uppgift: Implementera win conditions för ditt spel
# Exempel: första till X poäng, sista överlevande, etc.
pass
Fas 4: Implementera gamespecifik logik 🎲
Steg 1: Välj och implementera core gameplay
Baserat på ditt valda spelkoncept:
Din uppgift 11: Implementera huvudmekaniken
Välj EN av följande implementationer baserat på ditt spel:
För Combat/Battle Games:
# Lägg till i Player.gd
@export var weapon_damage: int = 25
@export var attack_range: float = 100.0
@export var attack_cooldown: float = 1.0
var can_attack: bool = true
func handle_combat_input():
if Input.is_action_just_pressed("attack") and can_attack:
perform_attack()
func perform_attack():
can_attack = false
# Hitta andra spelare inom räckvidd
var targets = find_targets_in_range()
for target in targets:
target.take_damage.rpc(weapon_damage, multiplayer.get_unique_id())
# Cooldown timer
await get_tree().create_timer(attack_cooldown).timeout
can_attack = true
func find_targets_in_range() -> Array:
var targets = []
# Din logik för att hitta spelare inom attack_range
return targets
För Racing/Movement Games:
# Lägg till i Player.gd
@export var boost_power: float = 2.0
@export var boost_duration: float = 2.0
var boost_available: bool = true
func handle_racing_input():
if Input.is_action_just_pressed("boost") and boost_available:
activate_boost()
@rpc("any_peer", "call_local")
func activate_boost():
if boost_available:
boost_available = false
speed *= boost_power
# Visual effect
$BoostEffect.visible = true
await get_tree().create_timer(boost_duration).timeout
speed /= boost_power
$BoostEffect.visible = false
# Cooldown
await get_tree().create_timer(5.0).timeout
boost_available = true
För Puzzle/Co-op Games:
# Lägg till i SceneManager.gd
var puzzle_state: Dictionary = {}
var required_players_for_action: int = 2
@rpc("any_peer", "call_local")
func player_interacted(player_id: int, object_id: String):
if not puzzle_state.has(object_id):
puzzle_state[object_id] = []
puzzle_state[object_id].append(player_id)
if puzzle_state[object_id].size() >= required_players_for_action:
solve_puzzle(object_id)
func solve_puzzle(object_id: String):
print("Puzzle solved by cooperation!")
# Din puzzle-lösning här
Steg 2: Implementera UI och feedback
Skapa spelinterface:
Din uppgift 12: Skapa GameUI.tscn och script
# GameUI.gd
extends Control
@onready var health_bar = $HealthBar
@onready var score_label = $ScoreLabel
@onready var timer_label = $TimerLabel
@onready var player_list = $PlayerList
var connected_players: Dictionary = {}
func _ready():
# Koppla till signals från SceneManager och Players
var scene_manager = get_node("/root/GameWorld/SceneManager")
scene_manager.game_started.connect(_on_game_started)
scene_manager.game_ended.connect(_on_game_ended)
func _on_game_started():
print("UI: Game started!")
# Visa spelspecifika UI element
func _on_game_ended():
print("UI: Game ended!")
# Visa resultat, return to lobby knapp, etc.
func update_health(new_health: int, max_health: int):
health_bar.value = (float(new_health) / max_health) * 100
func update_score(player_id: int, score: int):
# Uppdatera score display
pass
func update_timer(time_left: int):
timer_label.text = "Time: " + str(time_left)
Steg 3: Testa och balansera
Gör spelet roligt:
Din uppgift 13: Playtesting och iteration
# Playtesting Checklist
## Grundläggande funktionalitet:
- [ ] Två spelare kan ansluta
- [ ] Spelarrörelse synkroniseras korrekt
- [ ] Huvudmekanism fungerar (attack/race/puzzle/etc.)
- [ ] UI visar relevant information
- [ ] Spelet har tydlig win condition
## Network stability:
- [ ] Ingen rubber-banding (jumpig movement)
- [ ] Inga duplikerade objekt
- [ ] Graceful handling av disconnection
- [ ] Rimlig latency tolerance
## Gameplay balance:
- [ ] Spelet är rättvist för alla spelare
- [ ] Rounds är lagom långa (1-5 minuter?)
- [ ] Tydlig feedback på actions
- [ ] Roligt att spela igen
## Bug fixes att leta efter:
- [ ] Players spawnar på samma position
- [ ] Health/score synkas fel
- [ ] Input registreras flera gånger
- [ ] Game state blir osynkad
Fas 5: Polish och utökning 🎨
Steg 1: Lägg till juice och feedback
Gör spelet kännas bra:
Din uppgift 14: Implementera game feel
# ParticleEffects.gd - för visuell feedback
extends Node2D
@export var hit_particles: PackedScene
@export var explosion_particles: PackedScene
@rpc("any_peer", "call_local")
func spawn_hit_effect(position: Vector2):
var particles = hit_particles.instantiate()
get_tree().root.add_child(particles)
particles.global_position = position
particles.emitting = true
@rpc("any_peer", "call_local")
func spawn_explosion(position: Vector2):
var explosion = explosion_particles.instantiate()
get_tree().root.add_child(explosion)
explosion.global_position = position
explosion.emitting = true
Lägg till ljud:
# SoundManager.gd
extends Node
@export var attack_sound: AudioStream
@export var hit_sound: AudioStream
@export var victory_sound: AudioStream
@rpc("any_peer", "call_local")
func play_sound(sound_name: String):
match sound_name:
"attack":
$AttackPlayer.stream = attack_sound
$AttackPlayer.play()
"hit":
$HitPlayer.stream = hit_sound
$HitPlayer.play()
"victory":
$VictoryPlayer.stream = victory_sound
$VictoryPlayer.play()
Fas 6: Presentation och reflektion 🎤
Steg 1: Dokumentera ditt projekt
Visa upp ditt arbete:
Din uppgift 16: Skapa projekt-dokumentation
# Mitt Multiplayer-spel: [Titel]
## Spelkoncept
[Beskriv ditt färdiga spel]
## Teknisk implementation
### Networking features:
- [ ] Grundläggande peer-to-peer connection
- [ ] Player spawning och synkronisering
- [ ] [Din huvudmekanism]
- [ ] UI uppdateringar
- [ ] [Andra features du implementerat]
### Utmaningar jag löste:
1. [Problem och lösning]
2. [Problem och lösning]
3. [Problem och lösning]
### Skillnader från tutorial:
- [Vad ändrade/lade till du?]
- [Nya RPC-anrop du implementerade]
- [Nya game mechanics]
## Demo-video eller screenshots
[Lägg till bilder/video av spelet i action]
## Reflektion
### Vad lärde jag mig?
### Vad var svårast?
### Vad skulle jag göra annorlunda nästa gång?
### Nästa steg för detta projekt?
Steg 2: Slutgiltig presentation
Visa för andra:
Din uppgift 17: Förbered demo
# Demo-checklista
## Innan demo:
- [ ] Spelet fungerar stabilt med 2+ spelare
- [ ] Du kan förklara ditt spelkoncept på 30 sekunder
- [ ] Du har förberett demo-scenario (vad ska du visa?)
- [ ] Du vet hur du startar host/join snabbt
## Under demo:
- [ ] Förklara spelkonceptet
- [ ] Visa grundläggande multiplayer (två spelare ansluter)
- [ ] Demonstrera huvudmekanismen
- [ ] Förklara en teknisk utmaning du löste
- [ ] Visa vad som är unikt med ditt spel
## Diskussionspunkter:
- Hur skiljer sig ditt spel från tutorial?
- Vilka networking-koncept använde du?
- Vad var svårast att implementera?
- Hur skulle du skala upp för fler spelare?
Slutgiltiga utmaningar och nästa steg 🚀
Expert-level utbyggnader:
För de som vill gå steget längre:
- Dedicated Server: Implementera dedicated server istället för peer-to-peer
- Matchmaking: System för att hitta spelare automatiskt
- Persistent Stats: Spara spelarstatistik mellan sessioner
- Cross-platform: Testa på olika enheter/OS
- Optimization: Hantera 10+ spelare samtidigt
Networking-koncept du nu behärskar:
✅ Client-Server arkitektur
✅ RPC (Remote Procedure Calls)
✅ Multiplayer Authority
✅ State synchronization
✅ Latency compensation (lerping)
✅ Event-driven networking
✅ Peer management
Grattis! 🎉
Du har nu:
- ✅ Genomfört en etablerad multiplayer tutorial
- ✅ Planerat och designat ditt eget nätverksspel
- ✅ Implementerat custom gameplay over nätverk
- ✅ Löst tekniska networking-utmaningar
- ✅ Skapat en komplett multiplayer-upplevelse
Dessa skills är direkt överförbara till:
- Unity Netcode/Mirror
- Unreal multiplayer
- Web-baserade spel (WebRTC, WebSockets)
- Mobile multiplayer
- MMO development
Med förståelse för networking fundamentals kan du nu tackla vilken multiplayer-plattform som helst!
Nästa steg: Publicera ditt spel online eller utforska avancerade networking-koncept som dedicated servers och matchmaking!