My node setup for my game is:
I have a scene called RoomExit (seen in images below as the blue "door paths") and a scene called SpawnPosition (shown by the little crosshairs next to the door paths)
Level1 has a RoomExit on the right
Level2 has two RoomExits, one on the left of the room and one on the right)
Level3 has a single RoomExit on the left side (NOTE same coordinates as the left RoomExit on Level2)
When the player exits Level3 via the RoomExit on the left he appears in Level2 on the right-hand side as expected, however it then triggers the RoomExit collision on the LEFT hand side of Level2 which brings me to Level1....
The way my RoomExit works is when collided, I emit a "OnGotoRoom" on a "SignalBus" Singleton along with the room I wish to go to (ex "res://Rooms/TestLevel2.tscn") and the name of the Spawn Point node in that room (ex "Spawn002")
The "Game" node listens on that "OnGotoRoom" signal and
Instances the newRoom
Finds the GlobalPosition of the spawn object based on the name ("Spawn002")
Set's the player's GlobalPosition = spawn.GlobalPosition
Adds the newRoom to the "CurrentLevel" node
Calls QueueFree on the previous level
public void gotoRoom(string roomPath, string spawnPoint){
// Instance the new room
var newRoomScene = (PackedScene)ResourceLoader.Load(roomPath);
Room newRoom = (Room)newRoomScene.Instance();
//Set the Global Position of the character = Spawn point global position
PlayableCharacter player = GetNode<Node2D>("Player").GetChild<PlayableCharacter>(0);
player.GlobalPosition = newRoom.GetNode<Node2D>("PlayerSpawnPoints").GetNode<Node2D>(spawnPoint).GlobalPosition;
if (this.playerStats.character == "Robot"){
player.GlobalPosition = new Vector2(player.GlobalPosition.x, player.GlobalPosition.y-32);
}
//Add the newRoom to the "CurrentLevel" Node
GetNode<Node2D>("CurrentLevel").AddChild(newRoom);
//If there was already a level, Remove that node
if (GetNode<Node2D>("CurrentLevel").GetChildCount() != 1){
var current_room = GetNode<Node2D>("CurrentLevel").GetChild<Room>(0);
current_room.QueueFree();
}
//Move the current level to the first index
GetNode<Node2D>("CurrentLevel").MoveChild(newRoom, 0);
}
My Thoughts
I think what's going wrong is that when it moves the character from Level3 to Level2, the player's GlobalPosition isn't updated fast enough so he's colliding with the door on the left hand side (that's in the same position as the door he just exited on Level3).
So I briefly see him in Level2 on the right hand side where I want him, but he already collided with the door on the left because his position wasn't updated fast enough...
I verified this by moving the Left hand door on Level2 away and it works as expected.
I tried updating the character's GlobalPosition to new Vector2(-10000, -10000) BEFORE I emit the "OnGotoRoom" signal hoping that he'd not hit the door on the transition, but that didn't fix the problem.
I'm stumped on what to try next... any help will be much appreciated!
Related
I'm a beginner and I created a code where I can get my enemy's information, but I believe it is very simple and maybe wrong, since the more information I want to get, the more lines of code will be needed.
My idea is that when I get this contact between the player and the enemy (or object), I have this informations and can use it in combat math.
Another question is that the RayCast "works", however, I don't get this information in the first contact with the enemy.
Player code:
func _unhandled_input(event):
for dir in inputs.keys():
if event. is_action_pressed(dir):
move(dir)
func move(dir):
ray.cast_to = inputs[dir] * grid_size
ray.force_raycast_update()
if !ray.is_colliding():
position += inputs[dir] * grid_size
if ray.is_colliding():
if "Enemy" in ray.get_collider().get_groups():
var AC_enemy = ray.get_collider().AC
print("Colisão com inimigo")
print(AC_enemy)
In the enemy code I left only the value of the AC variable.
Thanks
hi I'm trying to make a frog that jumps when the player gets close I tried this
onready var playerpos = get_parent().get_node("player").position
And
onready var playerpos = get_parent().get_node("player").global_position
and get this error
Invaled get index 'position' (on base: 'null instance')
Null instance means that the object you are trying to get, doesn't exist. And that is, because you are asking for its position, before it even enters the scene.
However, what you are trying to do, is completely unnecessary. You already have a main scene, so that should be able to access all of its nodes global position, without having to call the entire object.
Access your player's position from the main scene, not the frog scene.
That is, if you want the players position. What you want to do, however, does not require that.
You need to have an area node attached to the frog, that detects and sends a signal, when it detects a physics object. After which you simply check, whether the physics object is player or not, and execute the appropriate action.
According to GDDoc below:
https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#literals
You can use the dollar sign to get node:
onready var player = $player
onready var player_pos = player.position
onready var player_global_pos = player.global_position
Or, you can get the player's position directly:
onready var player_pos = $player.position
onready var player_global_pos = $player.global_position
I've been making a FPS in Godot and I'm having a hard time getting the kinematic body (the enemy) to go towards the player. Could someone please help?
The simplest way to do this is to get the player's position, compare it to the enemy position, and make the enemy move towards it every frame.
Full example code is at the bottom.
To get the player's position you first need a reference to it. You can usually do this through storing the reference in global singleton (autoload) or by exposing a public property.
If you are doing it with a global singleton, then you get the position by calling var player_position = my_singleton.player.global_transform.origin
If you are using an exported property, then you would get the position by calling var player_position = get_node(path_to_player).global_transform.origin
Once you have the player position, you can compare it to the enemy by writing var direction_to_target = player_position - global_transform.origin from inside the enemy node.
Now in order to follow the player, we override the _physics_process method with something like this:
### Inside the enemy script
var ENEMY_SPEED= 50
func _physics_process(delta):
var player_position = my_singleton.player.global_transform.origin
var direction_to_target = (player_position - global_transform.origin).normalized() # We normalize the vector because we only care about the direction
move_and_slide(direction_to_target * ENEMY_SPEED) # We multiply the direction by the speed
I'm creating a little program that creates image patterns like these using line segments that rotate around each other:
image from Engare Steam store page for reference
How do I tell Godot to create instances of the Polygon2D scene I'm using as line segments with origins on Position2D nodes that exist as children in the Polygon2D scene? Here is a sample of my code:
const SHAPE_MASTER = preload("res://SpinningBar.tscn")
...
func bar_Maker(bar_num, parent_node):
for i in range(bar_num):
var GrabbedInstance = SHAPE_MASTER.instance()
parent_node.add_child(GrabbedInstance)
bar_Maker(bar_num - 1, $Polygon2D/RotPoint)
...
func _physics_process(delta):
...
if Input.is_action_just_pressed("Switch Item"): # from an older version of the program, just bound to ctrl
bar_Maker(segment_count, BarParent)
bar_num is the number of bars to instance, set elsewhere (range 1-6).
parent_node in the main scene is just a Node2D called BarParent.
The SpinningBar.tscn I'm instancing as GrabbedInstance has a Position2D node called "RotPoint" at the opposite end of the segment from the object's origin. This is the point I would like successive segments to rotate around (and also put particle emitters here to trace lines, but that is a trivial issue once this first one is answered).
Running as-is and creating more than 1 line segment returns "Attempt to call function 'add_child' in base 'null instance' on a null instance." Clearly I am adding the second (and later) segments incorrectly, so I know it's related to how I'm performing recursion/selecting new parents for segments 1+ node deep.
Bang your head against the wall and you will find the answer:
func bar_Maker(bar_num, parent_node):
for i in range(bar_num):
var GrabbedInstance = SHAPE_MASTER.instance()
parent_node.add_child(GrabbedInstance)
var new_children = GrabbedInstance.get_children()
bar_Maker(bar_num - 1, new_children[0])
If someone is aware of a more elegant way to do this please inform me and future readers. o7
I subclassed FL_Value_Input such that I can give the widget a color when it is modified, but the user has not yet pressed enter.
The silly thing is that the handle(int e) function is never invoked in case the event is a FL_KEYDOWN event, other events (such as FL_KEYUP, FL_DRAG, FL_FOCUS etc) are being propagated fine.
The widget is part of a widget hierarchy... Could it be that one of its parents in this hierarchy is absorbing this specific FL_KEYDOWN event?
EDIT: apparently, the widget also doesn't have focus (tested by comparing this to Fl::focus()), which is odd, as I am typing into it.
Any help would be appreciated.
Fx_Value_Input::Fx_Value_Input(int x, int y, int w, int h, const char* l)
: Fl_Value_Input(x, y, w, h, l)
{}
int Fx_Value_Input::handle(int e)
{
int r = Fl_Value_Input::handle(e);
if (e == FL_KEYBOARD)
{
if ((Fl::event_key() != FL_Enter && Fl::event_key() != FL_KP_Enter ) )
color(Fx::get_modified_color());
else if ((Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter) && color() == Fx::get_modified_color())
color(FL_WHITE);
redraw();
}
return r;
}
Using FL_KEYDOWN is the Windows way of doing things, which, as you have found, doesn't work because you need to subclass Fl_Input_: not Fl_Input and even then, it doesn't work properly.
Instead, use when(FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY). Check changed() when keys are pressed and clear_changed(). It isn't set when the enter key is pressed. You can change colours back when that condition is met.
inputkey = new Fl_Input...
inputkey->when(FL_WHEN_CHANGED| FL_WHEN_ENTER_KEY);
inputkey->callback(cb);
....
void cb(Fl_Widget *ob)
{
Fl_Input* ip = dynamic_cast<Fl_Input*>(ob);
if (ip->changed())
{
// change colour
ip->clear_changed();
}
else
{
// change colour
}
}
Got a reply on the FLTK Google group, explaining the issue... There is no easy workaround for this...
FLTK delivers key strokes directly to the focus widget. Fl_Value_Input
includes an Fl_Input widget, and that widget becomes the focus widget when you click in the input field.
Hence the "parent" Fl_Value_Input never sees the Fl_KEYDOWN events.
Note that I quoted "parent" because Fl_Value_Input is a very
special case. It's not derived from Fl_Group, but it behaves somewhat
like an Fl_Group widget.
So why does your derived widget get FL_KEYUP events? That's another
feature of FLTK: FL_KEYUP events are also delivered to the focus
widget in the first place. However, AFAICT no core FLTK widget
handles FL_KEYUP events - they all return 0 in their handle()
methods. If the focus widget doesn't handle an event FLTK tries to
deliver it to the parent widget and then up through the widget
hierarchy until it reaches the top level window. Hence (and this is
IMHO weird) eventually all widgets will receive all FL_KEYUP events
unless one widget returns 1 from its handle() method (which is
usually not the case). Depending on the depth of the focus widget in
the total widget hierarchy it may be that some widgets get the same
FL_KEYUP event multiple times, because all parent groups of the focus
widget try to deliver the event to all their children.