Global accessor State Machine Handler move_and_slide problem - godot

im currently having a problem with a statemachine that i am coding, the problem that is occurring is that when i add move_and_slide to the state of movement, the if condition at the last of the code block is getting the error:
the method " move_and_slide isn't declared in the current class
extends Node2D
class_name State
var state_machine = null
var player_velocity = Vector2()
var player_speed = 200
enum playerStates{IDLE, WALK, RUN, USE, SHOOT}
enum ItemStates{ITEM_ON_GROUND, IS_EQUIPED}
func _ready():
if Global.player_isIdle == State.state_machine == playerStates.IDLE:
pass
if Global.player_isWalking == State.state_machine == playerStates.WALK:
player_velocity = Global.player_velocity
func handle_input(_event: InputEvent):
if player_velocity != player_velocity.ZERO:
if Global.player_isWalking == State.state_machine == playerStates.WALK:
pass
if Global.player_isRunning == State.state_machine == playerStates.RUN:
if Input.is_action_pressed("sprint"):
player_speed = 300
Global.player_velocity = Global.player_velocity.normalized() * player_speed
Global.player_velocity = move_and_slide(Global.player_velocity)
func _physics_process(_delta):
pass

There is a method called move_and_slide but it belongs to KinematicBody2D (or its 3D counterpart).
However, you are writing code in a Node2D. As the error message tell you, there isn't a move_and_slide for Node2D.
Since there isn't a move_and_slide in Node2D, you could write your own method called move_and_slide that does whatever you want. However, I believe you want to use the one form KinematicBody2D.
So change the Node from Node2D to KinematicBody2D (and update the script, at the top, where it says extends Node2D to say KinematicBody2D). In the Godot editor you can find an option to change the type of a Node called "Change Type", it is in the contextual menu of the Node in Scene.

extends Node2D
class_name State
your class is extending Node2D but move_and_slide is from KinematicBody2D
Try:
extends KinematicBody2D
class_name State
and also use move_and_slide in _physics_update function.
Also, don't forget to change Player Node to KinematicBody2D
and if your Class is a child of Player node then do like this:
extends Node2D
class_name State
var state_machine = null
var player_velocity = Vector2()
var player_speed = 200
var parent = get_parent() <<<<<<<<<<<<<<<<
enum playerStates{IDLE, WALK, RUN, USE, SHOOT}
enum ItemStates{ITEM_ON_GROUND, IS_EQUIPED}
func _ready():
if Global.player_isIdle == State.state_machine == playerStates.IDLE:
pass
if Global.player_isWalking == State.state_machine == playerStates.WALK:
player_velocity = Global.player_velocity
func handle_input(_event: InputEvent):
if player_velocity != player_velocity.ZERO:
if Global.player_isWalking == State.state_machine == playerStates.WALK:
pass
if Global.player_isRunning == State.state_machine == playerStates.RUN:
if Input.is_action_pressed("sprint"):
player_speed = 300
Global.player_velocity = Global.player_velocity.normalized() * player_speed
func _physics_process(_delta):
Global.player_velocity = parent.move_and_slide(Global.player_velocity) <<<<<<<<<<

Related

Player not looking in the right direction

I have a player that you can move by clicking in a location. They are guided by pathfinding to also move around any potential obstacles in their way. Here is my script:
extends KinematicBody2D
export var speed = 200
var velocity = Vector2.ZERO
onready var navigation_agent = $NavigationAgent2D
func _ready():
navigation_agent.connect("velocity_computed", self, "move")
func _input(event):
if event.is_action_pressed("mouse_right"):
navigation_agent.set_target_location(event.get_global_position())
func _process(_delta):
if $RayCast2D.is_colliding():
if $RayCast2D.get_collider().is_in_group("enemy_ground_troop"):
self.velocity = Vector2.ZERO
ranged_attack()
if navigation_agent.is_navigation_finished():
return
var overlapping_areas = $EnemyDetector.get_overlapping_areas()
for area in overlapping_areas:
if area.is_in_group("enemy_ground_troop"):
navigation_agent.set_target_location(area.get_global_position())
look_at(area.get_global_position())
velocity = global_position.direction_to(navigation_agent.get_next_location()) * speed
look_at(global_position.direction_to(navigation_agent.get_next_location()))
$RayCast2D.global_rotation = self.global_rotation
navigation_agent.set_velocity(velocity)
func move(velocity):
velocity = move_and_slide(velocity)
func ranged_attack():
print_debug("fired ranged attack")
When I run the scene, the player is not looking where I want them too based on the look at commands. How can I fix this?
The look_at method takes a target position, not a direction.
So, instead of this:
look_at(global_position.direction_to(navigation_agent.get_next_location()))
Try this:
look_at(navigation_agent.get_next_location())

Switch between 2D layers programmatically

NOTE this is about Godot 4.0
In a particular scene, I've a set of layers, of which only one is visible at a time. My code is able to decide how a switch from one layer to other occurs.
I've implemented a class for solutioning this: ViewSwitch. That class has a set of ViewSwitchItem. The layers have to be a subtype of ViewSwitchItem.
The problem I'm facing is that, only once, from a layer first_menu, after I click the "Start Game" button in my first layer and switch to another layer start_game_menu, the game switches back to first_menu even if the player didn't meant so. Like I said, it happens only once. After you click like, say, the second time, you'll be transitioned to start_game_menu without being redirected to first_menu again. All this is done using GDScript. Something is wrong in my logic.
gd_util/ViewSwitch.gd
class_name ViewSwitch
var current_item: ViewSwitchItem = null
var items: Array[ViewSwitchItem] = []
func initialize() -> void:
swap(null)
for sw in items:
sw.parent_switch = self
sw.initialize()
func swap(swap: ViewSwitchItem) -> void:
if current_item == swap:
return
if current_item != null:
current_item.end(swap)
else:
immediate_swap(swap)
func immediate_swap(swap: ViewSwitchItem) -> void:
for sw in items:
sw.node.visible = false
if swap == null:
return
current_item = swap
swap.node.visible = true
swap.start()
gd_util/ViewSwitchItem.gd
class_name ViewSwitchItem
var parent_switch: ViewSwitch = null
var node: Node = null
func initialize():
pass
func start():
pass
func end(swap: ViewSwitchItem):
if parent_switch.current_item == swap:
return
immediate_swap(swap)
func immediate_swap(swap: ViewSwitchItem):
if parent_switch.current_item == swap:
return
parent_switch.immediate_swap(swap)
scenes/mainMenuScene/MainMenuScene.gd
extends Node2D
var view_switch: ViewSwitch = ViewSwitch.new()
var first_menu := MainMenuScene_firstMenu.new()
var start_game_menu := MainMenuScene_startGameMenu.new()
# Called when the node enters the scene tree for the first time.
func _ready():
view_switch.items = [
first_menu,
start_game_menu,
]
first_menu.node = $root/animation/container1
start_game_menu.node = $root/animation/container2_startGame
view_switch.initialize()
view_switch.swap(first_menu)
# first_menu
$root/animation/container1/startGameBtn.pressed.connect(func():
view_switch.swap(start_game_menu))
$root/animation/container1/exitBtn.pressed.connect(func():
get_tree().quit())
# start_game_menu
$root/animation/container2_startGame/panel/PanelContainer/VBoxContainer/MarginContainer/VBoxContainer/returnBtn.pressed.connect(func():
view_switch.swap(first_menu))
$root/animation.animation_finished.connect(func(animation_name):
if animation_name == "Anim":
view_switch.swap(first_menu))
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
# first_menu
if view_switch.current_item == first_menu:
pass
# start_game_menu
elif view_switch.current_item == start_game_menu:
if Input.is_action_just_released("back"):
view_switch.swap(first_menu)
scenes/mainMenuScene/switches/MainMenuScene_firstMenu.gd
class_name MainMenuScene_firstMenu
extends ViewSwitchItem
var start_game_button = null
func initialize():
start_game_button = self.node.get_tree().root.get_child(0).get_node("root/animation/container1/startGameBtn")
func start():
start_game_button.grab_focus()
func end(swap: ViewSwitchItem):
immediate_swap(swap)
scenes/mainMenuScene/switches/MainMenuScene_startGameMenu.gd
class_name MainMenuScene_startGameMenu
extends ViewSwitchItem
var panel1 = null
func initialize():
panel1 = self.node.get_tree().root.get_child(0).get_node("root/animation/container2_startGame/panel")
panel1.after_popup.connect(func():
self.node.get_tree().root.get_child(0).get_node("root/animation/container2_startGame/panel/PanelContainer/VBoxContainer/MarginContainer/VBoxContainer/returnBtn").grab_focus())
panel1.after_collapse.connect(func():
# switch to first_menu
immediate_swap(parent_switch.items[0]))
func start():
panel1.popup()
func end(swap: ViewSwitchItem):
panel1.collapse()
Thanks!

How do I make the player spawn next to car here

extends KinematicBody2D
var active = false
var car_zone = false
#car driving
func get_car_input():
var velocity = Vector2.ZERO
var speed = 200
if Input.is_action_pressed("forward"):
velocity.y = -1
if Input.is_action_pressed("backward"):
velocity.y = 1
if Input.is_action_pressed("left"):
velocity.x = -1
if Input.is_action_pressed("right"):
velocity.x = 1
move_and_slide(velocity*speed)
func _physics_process(_delta):
if active:
get_car_input()
leaving_car()
if !active:
entering_car()
pass
#entering/exiting car
func _on_player_detect_body_entered(body):
if body.name == "player":
car_zone = true
func _on_player_detect_body_exited(body):
if body.name == "player":
car_zone = false
func entering_car():
if Input.is_action_just_pressed("interact") && car_zone == true:
var hidden_player = get_parent().get_node("player")
hidden_player.active = false
#$Camera.make_current()
active = true
print("car entered")
func leaving_car():
var vehicle = $"."
var hidden_player = get_parent().get_node("player")
#spawn player to car HERE
if car_zone == false && Input.is_action_just_pressed("interact"):
hidden_player.active = true
active = false
#hidden_player.global_transform.origin = newLoc
I followed this tutorial: https://www.youtube.com/watch?v=7VzBHbG8sqo, and at 14:41, it shows how to do it in godot 3d, but I need it in godot 2d. He used "var newLoc = vehicle.global_transform.origin - 2*vehicle.transform.basis.x" to do that, but it doesn't work in 2d
There is a pretty simple solution for your problem, which does not even contain any math to work.
There is a Node called Position2D, which you can add to your Car Scene. Place it, where you want your character should stand after leaving the vehicle (so as a driver on the left side of your car)
Because the node is in your car scene it will move along with the car and rotate as well, so its always right next to your car.
All we need to do now is getting the global_position of the Position2D and setting the global_position of our player to it.
To make it easier to receive the global_position of the Position2D Node, we can add a function to the car which returns exactly that. Saying your Car Scene looks like this:
Vehicle
Sprite
ExitPosition (Our Position2D node. Renamed for clearity)
The function in our vehicle.gd could be like this:
func get_exit_location() -> Vector2:
return $ExitPosition.global_position
As I see it you have a variable named vehicle in your player code, which points to your car. So now, when you want to leave the car you can set the player position like this:
## Calling inside player code
global_position = vehicle.get_exit_location()
Keep in mind, that both ways (the one in the video and this one here) will make problems if there is something at the point your trying to place your player. So always check if your player can be at that position.

Invalid get index '1' (on base: 'Array') GODOT path finding

I'm trying to make a bird that follows a Position2D node attached to the character.(The Position2D node is in group called birdpos) When I run the game as soon as the bird is on screen (screendetector) it goes to the Position2D node. However once it reaches its destination it gives me the error "Invalid get index '1' (on base: 'Array')." (im also getting jittering when it reaches position) Im sure this is an easy fix, here is my code
extends KinematicBody2D
export(int) var SPEED: int = 100
var velocity: Vector2 = Vector2.ZERO
var path: Array = []
var levelNavigation: Navigation2D = null
var birdpos = null
onready var line2d = $Line2D #shows pathing
func _ready():
var tree = get_tree()
$flyingsprite1/AnimationPlayer.play("flying")
if tree.has_group("LevelNavigation"):
levelNavigation = tree.get_nodes_in_group("LevelNavigation")[0]
func move():
velocity = move_and_slide(velocity)
func _on_screenchecker_area_entered(area):
$Timer.start()
print("ligma")
yield(get_tree(), "idle_frame")
var tree = get_tree()
if tree.has_group("LevelNavigation"): #navigation node
levelNavigation = tree.get_nodes_in_group("LevelNavigation")[0]
if tree.has_group("birdpos"): #Position2D that is attached to player
birdpos = tree.get_nodes_in_group("birdpos")[0]
func _on_screenchecker_area_exited(area):
print("liga")
$Timer.stop()
var birdpos = null
var levelNavigation: Navigation2D = null
func _on_Timer_timeout():
line2d.global_position = Vector2.ZERO
if birdpos and levelNavigation:
generate_path()
func _physics_process(delta):
if Global.player.facing_right == true:
$flyingsprite1.scale.x = -1
else:
$flyingsprite1.scale.x = 1
if birdpos and levelNavigation:
navigate()
move()
func generate_path():
if levelNavigation != null and birdpos != null:
path = levelNavigation.get_simple_path(global_position, birdpos.global_position, false)
line2d.points = path
func navigate():
if path.size() > 0:
velocity = global_position.direction_to(path[1]) * SPEED
if global_position == path[0]:
path.pop_front()
edit: Updated Code
extends KinematicBody2D
export(int) var SPEED: int = 200
var velocity: Vector2 = Vector2.ZERO
var path: Array = []
var levelNavigation: Navigation2D = null
var birdpos = null
onready var line2d = $Line2D
func _ready():
# speed is distance over time
var tree = get_tree()
$flyingsprite1/AnimationPlayer.play("flying")
#if tree.has_group("Player"):
#player = tree.get_nodes_in_group("Player")[0]
func _on_screenchecker_area_exited(area):
$Timer.stop()
var birdpos = null
var levelNavigation: Navigation2D = null
func _on_Timer_timeout():
line2d.global_position = Vector2.ZERO
if birdpos and levelNavigation:
generate_path()
func _physics_process(delta):
if path.size() == 0:
return
var levelNavigation: Navigation2D = null
var birdpos = null
var next := global_position.move_toward(path[0], SPEED * delta)
var displacement := next - global_position
# And divide by it delta to get velocity:
move_and_slide(displacement/delta)
if Global.player.facing_right == true:
$flyingsprite1.scale.x = -1
else:
$flyingsprite1.scale.x = 1
if birdpos and levelNavigation:
navigate()
move()
func _input(event):
if Input.is_key_pressed(KEY_Q):
var tree = get_tree()
func _on_screenchecker_area_entered(area):
$Timer.start()
yield(get_tree(), "idle_frame")
var tree = get_tree()
if tree.has_group("LevelNavigation"):
levelNavigation = tree.get_nodes_in_group("LevelNavigation")[0]
if tree.has_group("birdpos"):
birdpos = tree.get_nodes_in_group("birdpos")[0]
func generate_path():
if levelNavigation != null and birdpos != null:
if is_instance_valid(birdpos):
path = levelNavigation.get_simple_path(global_position, birdpos.global_position, false)
line2d.points = path
func navigate():
if path.size() > 1:
velocity = global_position.direction_to(path[1]) * SPEED
if path.size() == 0:
path.pop_front()
func move():
if path.size() == 0:
return
velocity = move_and_slide(velocity)
The error
In this code:
if path.size() > 0:
velocity = global_position.direction_to(path[1]) * SPEED
if global_position == path[0]:
path.pop_front()
You check if the path has more than 0 points with path.size() > 0. That is, you are checking if the path has at least 1 point.
But to access path[1], the path must have at least 2 points.
Thus, if the path has exactly 1 point, it will pass the check path.size() > 0 and fail when reading path[1].
I don't know when the path would have exactly one point. It is not documented how this could happen, and it could be a problem with the navigation, it could even be a bug in Godot. But, as far as I can tell it is happening for you.
Presumably you want to reach path[0] instead of path[1] since that is what you are checking against to remove points.
If you do in fact want path[1], then check if the path has at least 2 points with path.size() > 1 (or path.size() >= 2 if you prefer).
The jitter
I'm assuming here that path[0] is the target.
I believe it is three things:
You cannot trust vector equality
Vector equality boils down to equality of the components. Which is float equality. And thus Vector equality has all the problems of float equality.
So, to compare to your current target use is_equal_approx. For example global_position.is_equal_approx(path[0]).
You don't want to move if you reached the target
This is easy enough: If there are no more points in path, don't move. That is, you can add this at the start of move:
if path.size() == 0:
return
If you will have the code in _physics_process instead of move, remember to check there.
You don't want to overshoot
So, move_and_slide will move the object as much as it should given the time between physics frames (delta). But that might be more than necessary to reach the target. As consequence, it is very likely to overshoot the target. As a result, the next physics frame it will have to move in the opposite direction, and overshoot again, and so on… Jitter!
I'll give you three solutions:
Don't use move_and_slide (with the caveat that you would be forgoing physics collisions):
# you can use move_toward
global_position = global_position.move_toward(path[0], SPEED * delta)
Let us keep move_and_slide, but compute the displacement we want.
# you can use move_toward
var next := global_position.move_toward(path[0], SPEED * delta)
# Then compute the displacement
var displacement := next - global_position
# And divide by it delta to get velocity:
move_and_slide(displacement/delta)
Again, using move_and_slide, but this time we figure out the maximum speed to not overshoot:
# speed is distance over time
var max_speed := global_position.distance_to(path[0])/delta
# And we clamp!
move_and_slide(velocity.clamped(max_speed))
For the versions of the code that use delta you can either put the code in _physics_process, or pass delta to move as a parameter. Also don't forget the check for path.size() mentioned in the prior point.
Addendum If you use path[0] as target, but it was equal to the current position, you would get about no velocity, and have to waste a physics frame. Consider this rewrite:
if path.size() > 0 and global_position.is_equal_approx(path[0]):
path.pop_front()
if path.size() > 0:
velocity = global_position.direction_to(path[0]) * SPEED

my get_viewport().get_mouse_position() isn't working right godot GDscript

I'm trying to make a 2d platformer where you spawn an object and try to jump on it in the air before it falls.
the problem is when I try to spawn the tile it doesn't spawn where Is the cursor at
it adds a relative value to the position that I don't know how to get rid of it.
you see when I try to instance a scene it takes the cursor position and viewport value into account but then something happens and I fount the object spawning way too far.
see where is the cursor at and where did the tile spawn
, same thing here
, and here
-here is how I'm grouping the nodes and scenes-
and this is the script I'm using, it's in the player1 scene
extends KinematicBody2D
#
var score : int = 0
export var speed : int = 200
export var jumpforce : int = 600
export var gravity : int = 800
onready var AB1 = preload("res://player1AB.tscn")
var vel :Vector2 = Vector2()
onready var sprite : Sprite = get_node("sprite_idile")
onready var ui : Node = get_node("/root/mainscene1/CanvasLayer/ui")
onready var audioplayer : Node = get_node("/root/mainscene1/Camera2D/audio_player")
func _physics_process(delta):
vel.x = 0
# movement inputs
if Input.is_action_pressed("move_left"):
vel.x -= speed
if Input.is_action_pressed("move_right"):
vel.x += speed
# applying the velcoty
vel = move_and_slide(vel,Vector2.UP)
#apllying gravty
vel.y += gravity * delta
#jump input
if Input.is_action_just_pressed("jump") and is_on_floor():
vel.y -= jumpforce
# where the sprite facing
if vel.x < 0:
sprite.flip_h = true
if vel.x > 0:
sprite.flip_h = false
if Input.is_action_just_pressed("restart"):
death()
func death ():
get_tree().reload_current_scene()
func collect_coin (value):
score += value
ui.set_score_text(score)
audioplayer.play_coin_sfx()
func _input(event):
if event.is_action_pressed("click"):
var ABT1 = AB1.instance()
add_child(ABT1)
var XN = null
XN = get_viewport().get_mouse_position()
ABT1.position = XN
important stuff
onready var AB1 = preload("res://player1AB.tscn")
func _input(event):
if event.is_action_pressed("click"):
var ABT1 = AB1.instance()
add_child(ABT1)
var XN = null
XN = get_viewport().get_mouse_position()
ABT1.position = XN
this the same problem in Godot form check it out if possible in case someone answered there
https://godotforums.org/discussion/27007/my-get-viewport-get-mouse-position-isnt-working-right#latest
If you don't have an extra Viewport
My first intuition is to get get_global_mouse_position and set global_position. That way you don't have to deal with any relative positioning:
ABT1.global_position = get_global_mouse_position()
Alternatively, you can check if the event is InputEventMouse, make it local with make_input_local, and get InputEventMouse.position from it:
func _input(event):
if event is InputEventMouse and event.is_action_pressed("click"):
var ABT1 = AB1.instance()
add_child(ABT1)
event = make_input_local(event)
ABT1.position = event.position
This approach would make it easier to then add touch support, because it does not rely on any function that gives you the mouse position. See my answer for Holding screen touch in godot.
If you have an extra Viewport
First of all, make sure you put your Viewport inside a ViewportContainer (otherwise it does not get input, see _input not called for a node inside a Viewport). Then we can use that ViewportContainer to get our coordinates.
Something like this:
func _input(event):
if event is InputEventMouse and event.is_action_pressed("click"):
var ABT1 = AB1.instance()
add_child(ABT1)
var container = find_parent("ViewportContainer") as ViewportContainer
if container != null:
event = container.make_input_local(event)
event = make_input_local(event)
ABT1.position = event.position
Notice there I'm using the function find_parent. It matches by name of the Node, not by type. Another approach you can try is using get_viewport().get_parent().
And yes, that should work regardless of stretch mode.

Resources