User Tools

Site Tools


godot

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
godot [2024/03/18 12:07]
admin
godot [2024/09/09 13:50] (current)
admin [Part 5]
Line 1: Line 1:
 ====== Getting Started with Godot ====== ====== Getting Started with Godot ======
 +This event runs monthly on the **2nd Monday** of the month. ​ See the [[start|home page]] for the date of our next monthly event.
  
 For our regular monthly tutorial nights, we're basing our teaching on the wonderful series by [[https://​www.youtube.com/​@AJsLearningLab|AJ'​s Learning Lab]], [[https://​www.youtube.com/​playlist?​list=PL4vjw0qHwNZIQZScBFaON0WGkz-BMyoCh|Godot FPS Tutorial - Learn Godot by making Wolfenstein 3D]]. For our regular monthly tutorial nights, we're basing our teaching on the wonderful series by [[https://​www.youtube.com/​@AJsLearningLab|AJ'​s Learning Lab]], [[https://​www.youtube.com/​playlist?​list=PL4vjw0qHwNZIQZScBFaON0WGkz-BMyoCh|Godot FPS Tutorial - Learn Godot by making Wolfenstein 3D]].
Line 6: Line 7:
  
 All the code we use is available on AJ's Learning Lab github - https://​github.com/​AJsLearningLab/​GodotFPS All the code we use is available on AJ's Learning Lab github - https://​github.com/​AJsLearningLab/​GodotFPS
 +
 +Glasgow Social Game Dev Chatroom Link (Matrix): https://​glasgow.social/​matrix
  
 ===== First Steps ===== ===== First Steps =====
Line 42: Line 45:
   * In the world scene, drag the player scene into the world   * In the world scene, drag the player scene into the world
   * In the three dot menu (world scene), use 'Add Sun to scene',​ 'add environment to scene'   * In the three dot menu (world scene), use 'Add Sun to scene',​ 'add environment to scene'
-  * In the player scene, use the Mesh button to add a '​Simplified ​Convext ​Collision Sibling'​ mesh to the player'​s MeshInstance3D+  * In the player scene, use the Mesh button to add a '​Simplified ​Convex ​Collision Sibling'​ mesh to the player'​s MeshInstance3D ​(not '​Single',​ that'​ll cause performance issues - why?)
   * Set up WASD controls in the Project Settings   * Set up WASD controls in the Project Settings
  
Line 103: Line 106:
   $AnimatedSprite2D.play("​knife_idle"​)   $AnimatedSprite2D.play("​knife_idle"​)
 </​code>​ </​code>​
-  * Set the ammo and current_weapon to '​gun'​. ​ Test.+  * Set the ammo to '​3' ​and current_weapon to '​gun'​. ​ Test.
   * Create a global script. ​ File -> New Script (based on Node). ​ Name it global.   * Create a global script. ​ File -> New Script (based on Node). ​ Name it global.
-  * Move the ammo and current weapon variables from the player ​script to the global script.+  * Move the ammo and current weapon variables from the ui script to the global script.
   * Open the project settings, in Autoload, add the global script and mark it global   * Open the project settings, in Autoload, add the global script and mark it global
   * Tweak the project setting page, allow fullscreen too   * Tweak the project setting page, allow fullscreen too
   * Edit the ui.gd script and change all references to current_weapon and ammo to be Global.current_weapon and Global.ammo   * Edit the ui.gd script and change all references to current_weapon and ammo to be Global.current_weapon and Global.ammo
   * Test everything still works   * Test everything still works
 +
 +==== Part 7 ====
 +**Goal: Add enemies**
 +  * Create new scene. ​ Other node->​CharacterBody3D. ​ Name it guard.
 +  * Add two children to the enemy node.  A CollisionShape3D (set to capsule), and a AnimatedSprite3D.
 +  * Set the Y to 1, to make it on the floor. ​ Adjust the capsule size to be 2m.
 +  * Import the guard sprite to the file system.
 +  * Select the AnimatedSprite3D,​ Click Sprite Frame-> New Sprite Frame. ​ Click it again to set a default animation using the grid, set to 7x2
 +  * Adjust the eize, choose AnimatedSprite3D. ​ Transform -> y =1, scale = 4 ish.
 +  * A new animations, '​die'​ and '​shoot'​
 +  * Select the AnimatedSprite3D,​ in the inspector find '​Flags',​ enable '​Billboard - Y:​Billboard'​
 +  * Edit the player script and add this line to the _ready() function:
 +<​code>​
 +add_to_group("​player"​)
 +</​code>​
 +  * Select the guard root node.  Create a new script, use the code below:
 +<code guard.gd>​
 +extends CharacterBody3D
 +
 +@onready var player : CharacterBody3D = get_tree().get_first_node_in_group("​player"​)
 +
 +const SPEED = 5.0
 +var gravity = ProjectSettings.get_setting("​physics/​3d/​default_gravity"​) ​ # Get the gravity from the project settings to be synced with RigidBody nodes.
 +var dead = false
 +var is_attacking = false  ​
 +var attack_range = 5
 +
 +func _ready():
 + add_to_group("​enemy"​)
 +
 +func _physics_process(delta):​
 + if dead or is_attacking: ​ # Check if the enemy is dead or attacking
 + return
 +
 + if player == null:
 + return
 +
 + var dir = player.global_position - global_position
 + dir.y = 0.0
 + dir = dir.normalized()
 +
 + velocity = dir * SPEED
 + # Add the gravity.
 + if not is_on_floor():​
 + velocity.y -= gravity * delta
 +
 + move_and_slide()
 + attack()
 +
 +func attack():
 + var dist_to_player = global_position.distance_to(player.global_position)
 + if dist_to_player > attack_range:​
 + return
 + else:
 + is_attacking = true  # Set the attacking flag
 + $AnimatedSprite3D.play("​shoot"​)
 + await $AnimatedSprite3D.animation_finished ​ # Wait for the animation to finish
 + is_attacking = false  # Reset the attacking flag
 +
 +
 +func die():
 + dead = true  # Corrected variable scope
 + $AnimatedSprite3D.play("​die"​)
 + $CollisionShape3D.disabled = true
 +
 +</​code>​
 +
  
 ==== Part 6 ==== ==== Part 6 ====
Line 131: Line 201:
 if Global.current_weapon != "​knife"​ and Global.ammo <= 0: if Global.current_weapon != "​knife"​ and Global.ammo <= 0:
   Global.current_weapon = "​knife"​   Global.current_weapon = "​knife"​
-  $AnimatedSprite2D.player("​knife_idle"​)+  $AnimatedSprite2D.play("​knife_idle"​)
 </​code>​ </​code>​
   * Tweak first if statement, change to:   * Tweak first if statement, change to:
Line 163: Line 233:
   $AnimatedSprite2D.play(Global.current_weapon + "​_idle"​)   $AnimatedSprite2D.play(Global.current_weapon + "​_idle"​)
 </​code>​ </​code>​
-==== Part 7 ==== 
  
-<​code ​guard.gd> +The full code for ui.gd should now be: 
-extends ​CharacterBody3D+<​code ​file ui.gd> 
 +extends ​CanvasLayer
  
-@onready ​var player : CharacterBody3D ​get_tree().get_first_node_in_group("​player"​)+var time_since_last_shot ​0.
 +var fire_rate = 1.0
  
-const SPEED = 5.0 
-var gravity = ProjectSettings.get_setting("​physics/​3d/​default_gravity"​) ​ # Get the gravity from the project settings to be synced with RigidBody nodes. 
-var dead = false 
-var is_attacking = false  ​ 
-var attack_range = 5 
  
 func _ready(): func _ready():
- add_to_group("enemy")+ $AnimatedSprite2D.animation_finished.connect(_on_AnimatedSprite2D_animation_finished) 
 + $AnimatedSprite2D.play(Global.current_weapon + "_idle")
  
-func _physics_process(delta): +func _process(delta): 
- if dead or is_attacking: ​ # Check if the enemy is dead or attacking + time_since_last_shot +delta 
- return + var can_shoot ​time_since_last_shot >(1.0 / fire_rate)
- +
- if player ​== null: +
- return +
-  +
- var dir player.global_position - global_position +
- dir.y ​0.0 +
- dir = dir.normalized()+
   
- velocity ​dir * SPEED + if Global.current_weapon !"​knife"​ and Global.ammo <= 0: 
- # Add the gravity+ Global.current_weapon = "​knife"​ 
- if not is_on_floor(): + $AnimatedSprite2D.play("​knife_idle"​) 
- velocity.-= gravity * delta+  
 + if Input.is_action_pressed("​ui_select"​and can_shoot
 +  
 + if Global.current_weapon == "​knife":​ 
 + $AnimatedSprite2D.play("​stab"​) 
 + else: 
 + $AnimatedSprite2D.play(Global.current_weapon + "​_shoot"​) 
 +  
 + time_since_last_shot = 0.0 
 +  
 + if Global.current_weapon != "​knife":​ 
 + if Global.ammo > 0: 
 + Global.ammo -= 
 +  
 + match Global.current_weapon:​ 
 + "​gun":​ 
 + fire_rate = 3.0 
 + "​machine":​ 
 + fire_rate = 6.0 
 + "​mini":​ 
 + fire_rate = 10.0 
 + "​knife":​ 
 + fire_rate = 2.0 
 + _: 
 + fire_rate = 1.0 
 +  
 +func _on_AnimatedSprite2D_animation_finished():​ 
 + $AnimatedSprite2D.play(Global.current_weapon + "​_idle"​)
  
- move_and_slide() 
- attack() 
  
-func attack(): +</code>
- var dist_to_player = global_position.distance_to(player.global_position) +
- if dist_to_player ​attack_range:​ +
- return +
- else: +
- is_attacking = true  # Set the attacking flag +
- $AnimatedSprite3D.play("​shoot"​) +
- await $AnimatedSprite3D.animation_finished ​ # Wait for the animation to finish +
- is_attacking = false  # Reset the attacking flag+
  
 +==== Part 8 ====
 +**Goal: Enemy Death and Raycasting**
 +  * Edit the ui.gd script. ​ Tweak the can_shoot variable, set it in the object scope (top) and remove "​var"​ from "var can_shoot"​ from the _process()
 +  * Access the 3D view of the player. ​ Add a new node as a child of the camera 3D, type: RayCast3D
 +  * Set the target position to y:0, z: -20
 +  * Make sure the raycast has access to collision mask layers 1+2
 +  * Check the guard scene, collision should be on layer 2, mask 1+2
 +  * Check Player is layer 1, mask is 1+2
 +  * Edit the player script, add new const at the top
 +<​code>​
 +@onready var ui_script = $ui
 +@onready var ray = $Camera3D/​RayCast3D
 +</​code>​
 +  * At the bottom, just above move_and_slide(),​ add the following code:
 +<​code>​
 +if Input.is_action_just_pressed("​ui_accept"​):​
 +  if ui_script.can_shoot:​
 +    shoot()
 +</​code>​
 +  * Add the shoot function
 +<code bash>
 +func shoot():
 +  if ray.is_colliding() and ray.get_collider().has_method("​die"​):​
 +    ray.get_collider().die()
 +</​code>​
 +  * To allow strafing, change the ''​If Input.is_action_just_pressed("​ui_accept"​):''​ to
 +<​code>​
 +if Input.is_action_pressed("​ui_accept"​):​
 +</​code>​
  
-func die(): +===== Part 9 ===== 
- dead true  # Corrected variable scope +**Goal: HUD labels and player death** 
- $AnimatedSprite3D.play("​die"​+  * Add a player health variable to the player.gd script 
- $CollisionShape3D.disabled = true+<​code>​ 
 +var player_health = 100 
 +</​code>​ 
 +  * Create two new Label nodes as a child of the UI, name one '​health'​. 
 +  * Create a new function in the ui.gd script 
 +<​code>​ 
 +function update_player_health():​ 
 +  $health.text = str(get_parent().player_health) 
 +</​code>​ 
 +  * Add the function to the bottom of the _process() function in the ui.gd script 
 +<​code>​ 
 +update_player_health() 
 +</​code>​ 
 +  * Edit the player script and add a new function: 
 +<​code>​ 
 +func damage(): 
 +  ​player_health -10 
 +  print(player_health) 
 +  if player_health <= 0: 
 +    queue_free() 
 +</​code>​ 
 +  * Add a RayCast3D to the guard scene 
 +  * Set Y = 0, Z = 5 
 +  * After the ''​play(shoot)''​ line in the attack() function, add: 
 +<​code>​ 
 +if $RayCast3D.is_colliding() and $RayCast3D.get_collider().has_method('​damage'​):​ 
 +  $RayCast3D.get_collider().damage() 
 +</​code>​
  
 +===== Part 10 =====
 +**Goal: Ammo pickups**
 +
 +
 +===== Next Steps =====
 +**Goal: Add sound effects**
 +  * Import the sound effect files (gun.ogg, machine.ogg,​ mini.ogg)
 +  * In the player scene, add a new node, AudioStreamPlayer.
 +  * Add the following code to the player shoot() function:
 +<​code>​
 +var sound_player = $AudioStreamPlayer
 +match Global.current_weapon:​
 +  "​gun":​
 +    sound_player.stream = preload("​res://​gun.ogg"​)
 +  "​machine":​
 +    sound_player.stream = preload("​res://​machine.ogg"​)
 +  "​mini":​
 +    sound_player.stream = preload("​res://​mini.ogg"​)
 +sound_player.play()
 </​code>​ </​code>​
godot.1710763623.txt.gz · Last modified: 2024/03/18 12:07 by admin