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
Related
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.
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.
this is my example code i also have Debug (visible collision shapes on) and I still can't see anything in game, my goal is to add a circle collision shape and be able to see in game
func create_area(mouse_position):
var position = mouse_position
var _shared_Area = Area2D.new()
var _circle_shape = Physics2DServer.circle_shape_create()
#_circle_shape.visible = true
Physics2DServer.shape_set_data(_circle_shape, 128)
print(" physics server ",Physics2DServer.ray_shape_create())
Physics2DServer.area_add_shape(_shared_Area.get_rid(), _circle_shape, xform )
Physics2DServer.area_set_collision_layer(_shared_Area, 1)
Physics2DServer.area_set_collision_mask(_shared_Area, 1)
Physics2DServer.area_set_monitorable(_shared_Area, true)
Physics2DServer.area_set_space(_shared_Area, get_world_2d().space)
Physics2DServer.area_set_transform(_shared_Area, Transform2D(1.0 , position))
Physics2DServer.area_set_area_monitor_callback(_shared_Area, self, 'debug_overlap')
func debug_overlap(_1,_2,_3,_4,_5):
print('overlap!!')
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.
I have created the following method to outline an 2D polygon using an Line2D node (in favor of _drawing because of texturing and round jointing capabilities of the Line2D node):
func Set_Polygon_Outline(_polygon_node: Node2D, _width: int = 5, _color: Color = Color.black, _texture: Texture = null) -> void:
if _polygon_node is Polygon2D:
var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
if _polygon.size() >= 3:
# Line2D node setup
var _line_node: Line2D = null
var _line_name: String = str(_polygon_node.name, "_Line")
if not _polygon_node.has_node(_line_name):
_line_node = Line2D.new() ; _line_node.name = _line_name ; _polygon_node.add_child(_line_node)
else: _line_node = _polygon_node.get_node(_line_name) as Line2D
# Line2D properties setup
if _line_node != null:
_line_node.width = _width ; _line_node.default_color = _color ; _line_node.joint_mode = Line2D.LINE_JOINT_ROUND
if _texture != null:
_line_node.texture = _texture ; _line_node.texture_mode = Line2D.LINE_TEXTURE_STRETCH
var _points: PoolVector2Array = _polygon ; _points.append(_polygon[0]) ; _line_node.points = _points
How would it be possible to replicate the round point jointing on point 0 in the same way as the other points?
The result meets expectations except on the closing points (from 4 to 0)
One approach I have tried is appending an additional point (point 1) to the _points Array. While the un-textured one looks as desired, the textured variant is slightly off on the additional line because of the two superimposing textures with alpha values making it look "bolder".
Another (very unorthodox approach) is creating two polygons: one black and one with an blur shader, using the following method:
func Set_Polygon_Shadow(_polygon_node: Node2D, _size: float = 10.0, _color: Color = Color.black) -> void:
if _polygon_node is Polygon2D:
var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
if _polygon.size() >= 3:
# Shadow Polygon node setup
var _shadow_name: String = str(_polygon_node.name, "_Shadow")
if not _polygon_node.has_node(_shadow_name):
var _shadow_node: Polygon2D = Polygon2D.new()
_shadow_node.polygon = Geometry.offset_polygon_2d(_polygon, _size).front() ; _shadow_node.color = _color
_shadow_node.show_behind_parent = true ; _polygon_node.add_child(_shadow_node)
# Blur Polygon node setup
var _blur_node: Polygon2D = Polygon2D.new()
_blur_node.polygon = Geometry.offset_polygon_2d(_polygon, _size * 2.0).front()
_blur_node.material = ShaderMaterial.new()
_blur_node.material.shader = preload("res://shaders/Shader_Blur.shader")
_blur_node.material.set_shader_param("Strength", 2.0)
_blur_node.show_behind_parent = true ; _polygon_node.add_child(_blur_node)
The shader code:
shader_type canvas_item;
uniform float Strength : hint_range(0.0, 5.0);
void fragment() {COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, Strength);}
The result looks good but I can't possibly imagine using this approach on a large number of polygons.
Thank you kindly,
Mike.
If using a Line2D is a viable solution, except for fixing the loop, then let us fix the loop.
For a Line2D to loop seamless, even with transparency, it must close in a straight segment (not a corner). And have no end caps.
Thus, I suggest to move the first point, half way between its original position and the second point. Then you add at the end the original position of the first point, following by a copy of its moved position...
Like this:
# Let o be a copy of the original first point
var o = _points[0]
# Let m be the middle of the straight segment between the first and second points
var m = o + (_points[1] - o) * 0.5
_points[0] = m # The line now starts in m, we are moving the first point forth
_points.append(o) # Since we moved the first point forth, add its original position back
_points.append(m) # Add m at the end, so it loops, in the middle of a straight segment
That should result in a Line2D that loops seamlessly.