Godot: global position of point is in a different scene - godot

I am creating a map with points, and if you click a point you go to it. The points are all connected by lines. The points are also randomly placed around the map, so I have to program in the functionality to draw lines from themselves to all nearby points. I have created a separate scene for these points, and am instancing them in the main scene. I can draw the lines, but instead of being drawn between the points, they are being drawn from it's parent point's location to the location of the point in its own scene. In other words, wherever I move the point in it's scene, the instanced points in the main scene will draw their lines to that location. Here is my code:
extends Node2D
var point_a = self.get_global_position()
var point_b = 0
var draw_line = false
func _ready():
pass
func _process(_delta):
var overlapping_areas = $Area2D.get_overlapping_areas()
for area in overlapping_areas:
if area.is_in_group("Location"):
point_b = area.get_global_position()
draw_line = true
update()
func _draw():
if draw_line:
draw_line(to_local(point_a), to_local(point_b), Color(1, 1, 1, 1), 2)
draw_line = false

Your problem is that point_a = Vector2(0,0) and to_local(Vector2(0,0)) is ending up being offset somewhere in the negatives. There was also a problem with initializing var point_a = self.get_global_position() because get_global_position will fail when the script loads because the Node has not been added to the SceneTree yet.
It's not necessary to calculate point_a at all, since draw_line's origin is the position of the object. It will always be Vector2(0,0), or Vector2.ZERO. Then, point_b starts as null because there is no valid end point until some areas overlap.
extends Node2D
var point_a = Vector2.ZERO
var point_b = null
func _physics_process(_delta):
var overlapping_areas = $Area2D.get_overlapping_areas()
for area in overlapping_areas:
if area.is_in_group("Location"):
point_b = area.get_global_position()
update()
func _draw():
if point_b != null:
draw_line(point_a, to_local(point_b), Color(1, 1, 1, 1), 2)
However, this logic may not give the desired result unless the paths are linear.
From your description, believe this is what you wanted:
extends Node2D
var point_a = Vector2.ZERO
var point_b_array = []
func _physics_process(_delta):
var overlapping_areas = $Area2D.get_overlapping_areas()
if overlapping_areas.size() > 0:
point_b_array.clear()
for area in overlapping_areas:
if area.is_in_group("Location"):
point_b_array.push_back(area.get_global_position())
update()
func _draw():
for end_point in point_b_array:
draw_line(point_a, to_local(end_point), Color(1, 1, 1, 1), 2)
Instead of using a single variable for point_b I used an array. When we find an overlapping area, it gets added into the array by push_back(), and in draw() we iterate over all of the points and draw lines to them. This also neatly solves the issue of drawing zero lines or what to do when draw is called before the scene is fully loaded.

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: get global position not getting correct position

I am trying to make a sort of movement map where all the points are connected by lines. But the points next to each other are the only ones being connected, which is what I am trying to do. However, whenever I try to draw a line between the two points, the lines are there but they go in a very different direction than where I want. I have been using get_global_position to get the position of the start and end of the line. Here is my code for the scene:
extends Area2D
var point_a = self.get_global_position()
var point_b = 0
var draw_line = false
func _process(delta):
var overlapping_areas = get_overlapping_areas()
for area in overlapping_areas:
if area.is_in_group("Location"):
point_b = area.get_global_position()
draw_line = true
update()
func _draw():
if draw_line:
draw_line(point_a, point_b, Color(1, 1, 1, 1), 2)
draw_line = false
I am new to Godot and coding so there may be something I am just missing.
The _draw function draws in the Node's local space, not in global space. Therefore one valid solution would be:
func _draw():
if draw_line:
draw_line(to_local(point_a), to_local(point_b), Color(1, 1, 1, 1), 2)
draw_line = false

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

Godot smooth transition between players

I have a "parent" player scene, and I inherit scenes for each player. The parent player scene has a camera. When the game switches between players, one player turns off its camera, and the other player turns its camera on:
if state != State.ACTIVE:
# If this player is becoming active, also
# set camera current
state = State.ACTIVE
camera.current = true
else:
# If player is not becoming active,
# disable this players camera
camera.current = false
But players can be in different positions, so the camera "jumps" from one to the other. Can we do something more sophisticated, like set the new camera to the current position so the smooth setting can be used to handle the transition?
One idea is to do get_viewport().get_camera() to find the current position of the camera to try and sync the position of the current camera with the new camera that is about to turn on, but appears to not work for 2D scenes. CF: https://github.com/godotengine/godot/pull/38317
Sadly, as you found out, there is no way to get the current Camera2D in Godot 3.x. And you found the pull request that adds the feature to Godot 4.0.
What I'm going to suggest is to have one sole Camera2D, so that one is always the current one. And you can define Position2D inside your scenes that can serve as interpolation targets to move the Camera2D.
I have an script that I think will be useful for you (I made it to be RemoteTransform2D but backwards, it does push a transform, it pulls it), I call it anchor_transform_2d.gd:
tool
class_name AnchorTransform2D
extends Node2D
export var anchor_path:NodePath setget set_anchor_path
export var reference_path:NodePath setget set_reference_path
export var set_local_transform:bool
export(int, FLAGS, "x", "y") var translation_mode:int
export(int, FLAGS, "x", "y") var scale_mode:int
export var rotation_mode:bool
var _anchor:Node2D
var _reference:Node2D
func _physics_process(_delta: float) -> void:
if not is_instance_valid(_anchor) or Engine.editor_hint:
set_physics_process(false)
return
#INPUT
var input := _anchor.global_transform
if is_instance_valid(_reference):
input = _reference.global_transform.affine_inverse() * input
#TRANSLATION
var origin := Vector2 (
input.origin.x if translation_mode & 1 else 0.0,
input.origin.y if translation_mode & 2 else 0.0
)
#ROTATION
var angle := 0.0
if rotation_mode:
angle = input.get_rotation()
#SCALE
var source_scale = input.get_scale()
var scaling := Vector2 (
source_scale.x if scale_mode & 16 else 1.0,
source_scale.y if scale_mode & 32 else 1.0
)
#RESULT
_set_target_transform(
Transform2D(angle, origin) * Transform2D.IDENTITY.scaled(scaling)
)
func set_anchor_path(new_value:NodePath) -> void:
anchor_path = new_value
if not is_inside_tree():
yield(self, "tree_entered")
_anchor = get_node_or_null(anchor_path) as Node2D
set_physics_process(is_instance_valid(_anchor) and not Engine.editor_hint)
if Engine.editor_hint:
update_configuration_warning()
func set_reference_path(new_value:NodePath) -> void:
reference_path = new_value
if not is_inside_tree():
yield(self, "tree_entered")
_reference = get_node_or_null(reference_path) as Node2D
func _set_target_transform(new_value:Transform2D) -> void:
if set_local_transform:
transform = new_value
return
global_transform = new_value
func _get_configuration_warning() -> String:
if _anchor == null:
return "Anchor not found"
return ""
Add this attached to a Node2D in anchor_path set the target from which you want to pull the transform (anchor_path is a NodePath, you can set to it something like $Position2D.get_path()). And set what do you want to copy (you can choose any combination of position x, position y, scaling x, scaling y, and rotation). Then put the Camera2D as a child of the AnchorTransform2D, and set smoothing_enabled to true.
Rundown of the properties:
anchor_path: A NodePath pointing to the Node2D you want to pull the transform from.
reference_path: A NodePath pointing to a Node2D used to make the transform relative (you will be taking the transform of what you put in anchor_path relative to what you put in reference_path).
set_local_transform: Set to true if you want to pull the transform as local (relative to the parent of AnchorTransform2D), leave to false to set the global transform instead.
translation_mode: Specifies if you are going to copy the x position, y position, both or neither.
scale_mode: Specifies if you are going to copy the x scale, y scale, both or neither.
rotation_mode: Specifies if you are going to copy the rotation or not.
The only reason the script is a tool script is to give you a warning in the editor if you forgot to set the anchor_path.

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