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

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.

Related

Raycast2D end not detecting collision

I have a Raycast2D where I want to detect collision with the enemy, but it does not collide at the end. What I'm saying is that it only detects collision at the place where it originates. Here is my code:
extends KinematicBody2D
export var speed = 200
var velocity = Vector2.ZERO
var enemy = 0
onready var navigation_agent = $NavigationAgent2D
onready var bullet = preload("res://Bullet.tscn").instance()
func _ready():
navigation_agent.connect("velocity_computed", self, "move")
$RayCast2D.global_rotation = self.global_rotation - 90
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"):
enemy = $RayCast2D.get_collider()
velocity = Vector2.ZERO
ranged_attack()
if navigation_agent.is_navigation_finished():
return
velocity = global_position.direction_to(navigation_agent.get_next_location()) * speed
look_at(navigation_agent.get_next_location())
navigation_agent.set_velocity(velocity)
func move(velocity):
velocity = move_and_slide(velocity)
func ranged_attack():
add_child(bullet)
bullet.global_position = self.global_position
bullet.target = enemy.global_position
Could someone help me fix this?
are you debugging the physics collisions during runtime? you should be able to see if the line is intersecting or not.
look under Debug > Visible Collision Shapes
also try
$RayCast2D.force_raycast_update() inside of _process and see if that makes some difference.
Are you sure the raycast is enabled under the inspector?

Godot apply_central_force direction is changing after the rigidbody2d is rotated

Simple top down mini golf game. I'm using a rigidbody2d as a ball, and a ray that points from the ball to the mouse. I use apply_central_force towards the mouse position (normalized) * speed. you can see this works fine at first. the ball goes in the direction of the ray. once it gets rotated it doesn't.
Gif of the issue
I tried changing the rigidbody mode to character but it sticks to the wall to much. I tried having a position node be the beginning of the ray and setting its rotation to 0 every frame but that didnt work either. Here is my scene tree and code. all code is in the "Planet" which is the rigidbody ball.
Scene Tree
extends RigidBody2D
const max_length = 2000
onready var beam = $Beam
onready var end = $end
onready var ray = $RayCast2D
onready var begin = $begin
onready var my_line = $Line2D
export var shot_speed = 10
export var default_speed = 10
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
shot_speed = default_speed
func _physics_process(delta):
_handle_ray()
func _handle_ray():
var mouse_pos = get_local_mouse_position()
var max_cast_to = mouse_pos.normalized() * max_length
ray.cast_to = max_cast_to
if ray.is_colliding():
end.global_position = ray.get_collision_point()
else:
end.global_position = ray.cast_to
beam.rotation = ray.cast_to.angle()
beam.region_rect.end.x = end.position.length()
if Input.is_action_pressed("Fire"):
shot_speed += 10
if Input.is_action_just_released("Fire"):
apply_central_impulse(ray.cast_to.normalized() * shot_speed)
shot_speed = default_speed

Kinematic Body 2D not moving

I'm trying to game a game using the Godot Engine but I'm stuck at the beginning! I can't make my KinematicBody2D move!
This is my Player.GD script
extends KinematicBody2D
var velocity = Vector2.ZERO
var move_speed = 480
var gravity = 1200
var jump_force = -720
var right = Input.is_action_pressed("move_right")
var left = Input.is_action_pressed("move_left")
var jump = Input.is_action_pressed("jump")
func _ready():
pass
func _physics_process(_delta):
var move_direction = int(right) - int(left)
velocity.x = move_speed * move_direction
move_and_collide(velocity)
Can someone, please, help me?
All this code will run when the KinematicBody2D is initialized:
var velocity = Vector2.ZERO
var move_speed = 480
var gravity = 1200
var jump_force = -720
var right = Input.is_action_pressed("move_right")
var left = Input.is_action_pressed("move_left")
var jump = Input.is_action_pressed("jump")
In consequence, it will not be taking input in real time. Instead you want the last three lines here:
func _physics_process(_delta):
var right = Input.is_action_pressed("move_right")
var left = Input.is_action_pressed("move_left")
var jump = Input.is_action_pressed("jump")
# …
Those are boolean, by the way. You can get a float from 0.0 to 1.0 if you use Input.get_action_strength instead. Which will also let your code ready for analog input.
I also want to point out that move_and_collide does not take a velocity, but a displacement vector. So to call it correctly, you want to multiply the velocity by delta:
func _physics_process(delta):
# …
move_and_collide(velocity * delta)
Or use move_and_slide, which does take a velocity. By the way, the up parameter that move_and_slide takes is to discern between floor, ceiling, and walls. Without, everything is considered a wall.

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

Is there a way to play an animation that one has exported from another scene?

I am currently working on an enemy spawner for a 2D arena wave spawner game in Godot.
I have an export(resource) variable that allows me to insert a spawning animation into the scene.
My question is: How do I play said animation?
I have created comments in my code that explains how it is set up. The two possible ways I think it could be solved is either by
Inserting a function where I commented it, that takes the previously determined position value and playing that animation at those coordinates. However I do not know how to play the animation as an export(resource) var.
Play the animation not as a function using the position value... but its still the same question.
Thanks!
extends Node2D
export(NodePath) var node_path
const WIDTH = 254
const HEIGHT = 126
export(Resource) var ENEMY
export(Resource) var SPAWNANIMATION
var spawnArea = Rect2()
var delta = 3
var offset = 0.5
#Creates the spawnArea and randomizes positions for the enemy to spawn
func _ready():
randomize()
spawnArea = Rect2(0, 0, WIDTH, HEIGHT)
setnextspawn()
#Spawns an Enemy at said Random position
func spawnEnemy():
var position = Vector2(randi()%WIDTH, randi()%HEIGHT)
var enemy = ENEMY.instance()
enemy.position = position #Determined position
#EnemySpawningAnim(position)
get_node(node_path).add_child(enemy)
return position
#Spawn Timer for in between each enemy spawn
func setnextspawn():
var nextTime = delta + (randf()-0.5) * 2 * offset
$Timer.wait_time = nextTime
$Timer.start()
func _on_Timer_timeout():
spawnEnemy()
setnextspawn()
#Function that takes a position to play the animation at
func EnemySpawningAnim(position):
pass
You'll need an AnimationPlayer node that you add_animation() resource when _ready(). Here's a code example:
func _ready():
$AnimationPlayer.add_animation('spawn', SPAWNANIMATION)
func EnemySpawningAnim(position):
# ... logic to handle position
$AnimationPlayer.play('spawn')

Resources