Use this space for questions related to what you're learning. For any other type of support (website, learning platform, payments, etc...) please get in touch using the contact form.
My very lazy solution to the challengeWalidI tought of just referencing the already created states and call their enter / update / exit , for the projectile I called it 5 times since I want 5 projectiles to be thrown but I had to tweak the StateFireProjectile:
AI.gd :
```gdscript
class StateFireProjectile extends State:
var spawning_point: Node3D = null
var projectile_scene: PackedScene = null
var spawning_anchor : Node3D = null
func _init(
init_mob: Mob3D,
init_spawning_point: Node3D,
init_spawning_anchor:Node3D,
init_projectile_scene: PackedScene
) -> void:
super("Fire Projectile", init_mob)
spawning_point = init_spawning_point
spawning_anchor = init_spawning_anchor
projectile_scene = init_projectile_scene
func enter() -> void:
var projectile: Projectile3D = projectile_scene.instantiate()
mob.add_sibling(projectile)
projectile.global_position = spawning_point.global_position
projectile.look_at(spawning_point.global_position + spawning_point.global_basis.z)
spawning_anchor.rotate_y(2 * PI/5)
finished.emit()
class StateChargeAndFire extends State:
var projectile_state : StateFireProjectile
var charge_state : StateCharge
var projectile_count := 5
func _init(init_mob : Mob3D , init_projectile_state : StateFireProjectile , init_charge_state : StateCharge) -> void:
super("Charge And Fire" , init_mob)
projectile_state = init_projectile_state
charge_state = init_charge_state
func enter() -> void :
for i in range(projectile_count):
projectile_state.enter()
charge_state.enter()
func update(delta) -> Events:
var charge_event = charge_state.update(delta)
if charge_event == Events.FINISHED :
return Events.FINISHED
return Events.NONE
func exit() -> void :
charge_state.exit()
```
mob_bee.gd
```gdscript
class_name MobBee3D extends Mob3D
@export var hit_box : HitBox3D
@onready var fire_point: Marker3D = %FirePoint
@onready var shooting_anchor: Node3D = %ShootingAnchor
func _ready() -> void:
const PROJECTILE_SCENE = preload("res://assets/entities/projectile/mob_fireball.tscn")
var state_machine := AI.StateMachine.new()
add_child(state_machine)
var idle := AI.StateIdle.new(self)
var chase := AI.StateChase.new(self)
chase.chase_speed = 3.0
var look_at_player := AI.StateLookAtPlayer.new(self)
look_at_player.duration = 2.0
var fire_projectile := AI.StateFireProjectile.new(self , fire_point, shooting_anchor , PROJECTILE_SCENE)
var charge := AI.StateCharge.new(self)
charge.charge_speed = 14.0
charge.charge_hit_box = hit_box
var wait_after_charge := AI.StateWait.new(self)
wait_after_charge.duration = 1.5
var charge_and_fire := AI.StateChargeAndFire.new(self ,fire_projectile , charge)
state_machine.transitions = {
idle: {
AI.Events.PLAYER_ENTERED_LINE_OF_SIGHT: chase,
},
chase: {
AI.Events.PLAYER_EXITED_LINE_OF_SIGHT: idle,
AI.Events.PLAYER_ENTERED_ATTACK_RANGE: look_at_player,
},
look_at_player: {
AI.Events.FINISHED: charge_and_fire,
},
charge_and_fire: {
AI.Events.FINISHED: wait_after_charge,
},
wait_after_charge: {
AI.Events.FINISHED: chase,
},
}
state_machine.activate(idle)
state_machine.is_debugging = true
```10May. 14, 2025
Bee velocityWalidI'm just asking if the velocity of the bee in the charge wouldn't be framerate independent because it's not multiplied by delta it s the increment of the traveled distance that is multiplied by delta 30May. 13, 2025
My solution to the challenge dim-pantherAt the start i was dividing the projectiles by 360, but i forgot that the rotation angle is in radians :)
I didnt want to mix charge with arc shooting, so i seperated it out in its own state, that immediately triggers charge after finished. Which could be more composable, i guess. Thanks for fun challenge
```gdscript
class StateFireProjectileArc extends State:
var projectile_count := 4
var spawning_point: Node3D = null
var projectile_scene: PackedScene = null
func _init(
init_mob: Mob3D,
init_spawning_point: Node3D,
init_projectile_scene: PackedScene
) -> void:
super("Fire Projectile", init_mob)
spawning_point = init_spawning_point
projectile_scene = init_projectile_scene
func enter() -> void:
var angle_step := (2.0 * PI) / projectile_count
for i in projectile_count:
var projectile: Projectile3D = projectile_scene.instantiate()
mob.add_sibling(projectile)
projectile.global_position = spawning_point.global_position
projectile.look_at(spawning_point.global_position + spawning_point.global_basis.z)
projectile.rotate_y(angle_step * i)
finished.emit()
```
10Apr. 30, 2025
Checking if the Solution is okayAbdul Hannan AhmedHi! We had this challenge where when the `bee` mob charges, it had to shoot projectiles in an arc. Here's my solution, I wanna know if this is fine.
```gdscript
class StateCharge extends State:
var charge_speed := 15.0
var charge_distance := 6.0
var _traveled_distance := 0.0
func _init(init_mob: Mob3D) -> void:
super("Charge", init_mob)
func enter() -> void:
_traveled_distance = 0.0
mob.skin.play("charge")
for i in range(5):
var projectile: Projectile3D = preload("res://assets/entities/projectile/mob_fireball.tscn").instantiate()
var random_angle := randf_range(0.0, 2.0 * PI)
mob.add_sibling(projectile)
projectile.global_position = mob.global_position
projectile.rotation.y = random_angle
func update(delta: float) -> Events:
mob.velocity = mob.global_basis.z * charge_speed
mob.move_and_slide()
if mob.get_slide_collision_count() > 0:
return Events.FINISHED
_traveled_distance += charge_distance * delta
if _traveled_distance >= charge_distance:
return Events.FINISHED
return Events.NONE
func exit() -> void:
mob.velocity = Vector3.ZERO
```10Mar. 12, 2025
Projectile challengeram876Hello! In the bee last projectile challenge, I did not create a new state, but slightly redid the fire_projectile state:
```gdscript
var angle := 0.0
for i in projectile_amount:
var projectile: Projectile3D = projectile_scene.instantiate()
mob.add_sibling(projectile)
projectile.global_position = spawning_point.global_position
projectile.look_at(projectile.global_position + spawning_point.global_basis.z)
projectile.rotate_y(angle)
angle += 2 * PI / projectile_amount
```70Feb. 09, 2025
The bee gets stuck when chargeram876Hello! When I started the level after encoding the charge state, the bee was only twitching in place, I later found out that when charging, the bee collides with the box. This happens because the velocity vector is pointing slightly down (I disabled the bee's collision with the world). I've learned from experience that this happens during the chase state. Further, based on the previous module, I found out that it is necessary to assign zero to the desired velocity.y. Now the bee does not collide with the box and it rams the player successfully.10Feb. 09, 2025
ChargeAndShoot challenge - Projectiles's angle mathsKorielGreetings! So I had a go to this lesson's last challenge, and I came up with a very simple solution for spawning x amount of projectiles in a circle:
```gdscript
for i in projectile_amount:
var spawn_angle_increment := (2 * PI) / projectile_amount
var new_projectile := ProjectileScene.instantiate()
mob.add_sibling(new_projectile)
new_projectile.global_transform = mob.global_transform
new_projectile.rotate_y(i * spawn_angle_increment)
```
Then I went to check on the provided solution in the solutions project and - to me, someone with no maths knowledge whatsoever - seemed like a strange and overcomplicated solution which seemed to produce similar results but with more code.
```gdscript
var angle_interval := arc_angle / projectile_count
var half_arc := arc_angle / 2.0
for i in range(projectile_count):
var projectile := projectile_scene.instantiate()
mob.add_sibling(projectile)
projectile.global_position = mob.global_position
projectile.global_basis = mob.global_basis
projectile.rotate_y(half_arc - i * angle_interval)
```
I just can't figure out the exact reason to this approach, and I'd like to learn the why, since I imagine it does have a benefit that I'm not seeing.
Thank you!10Nov. 20, 2024
Not collidingRudiHi hi. I added a HitBox onto the bee, and I am doing the collision count `mob.get_slide_collision_count()` after the `move_and_slide`, but when it intersects the player, the player does indeed take damage. My expectation is that the bee would stop at that point, but it continues through the player and ends at the distance it would have normally finished.
How can I ensure that the collision does indeed work? In an earlier session, we set the bee's collider base collider to no layers.
I did add the player-level mask on the base Character3D collision section and that does work.20Oct. 01, 2024
Lesson Q&A
Use this space for questions related to what you're learning. For any other type of support (website, learning platform, payments, etc...) please get in touch using the contact form.