Two lil questions.

JohynJohyn Posts: 48Member
edited April 7 in 2D

Hello,
I got a kinematic player moving on a minimap by mouseclick, and changing scenes to special places, then coming back to minimap.
Trouble is, when coming back, the player globalposition is reset to the one at the begining of the scene, not where he left the minimap. How to keep that position please?
Here's the script:

And, how to set the movement speed of my player regular, in the stead of exponential. That is, slow from a close click, and fast from a far one?

«1

Answers

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    You want your player to stay at the same position when returning to a scene with the mini-map, correct?

    For storing/saving variables across scenes, I’d suggest using an auto load script, setting the mini-map position right before you change scenes, and then reading it in. See this page from the docs on how to use auto load scripts.

    Then you’d need to this something like this before you change scenes:

    var singleton = get_node(“/root/singleton_name_here”)
    Singleton.mini_map_position = get_node(“path_to_player_here”).global_position
    “ Change scenes! ”

    Then in your player, you just add this to your _ready function in your mini-map:

    func _ready():
        var singleton = get_node(“/root/singleton_name_here”)
        if singleton.mini_map_position != null:
            $Map/Sprite.global_position = singleton.mini_map_position

    You just need to set mini_map_position to null anytime you want to start at the default position in the scene.


    I’m not sure on how to change the movement speed. I haven’t really used Tween nodes (at all, actually) so I’m making a totally guess here, but I’d say you need to use the distance between the player’s current position and the position your wanting to go to, and then somehow use that to change the speed accordingly.

  • JohynJohyn Posts: 48Member

    Correct, I'll check that singleton better, and tinker a bit with that movement speed.
    Thxs a lot!

  • JohynJohyn Posts: 48Member

    Sry... Got my script 'Global' saved as Map in autoload, but once I write that line (var singleton = get_node(“/root/Map”)) somewhere, wether in my player script or... elsewhere, I got that parse error: unknown character.
    Assuming that Map is the correct name for the singleton path, as it is the one saved in autoload...

  • JohynJohyn Posts: 48Member
    edited April 7

    Ok, copying wrong commas...

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    No worries!

    An unknown character means something is messed up with how it's being parsed. If it could not find the singleton, then it would return null and/or give a node not found error. Here is what I would try:

    | Try just writing get_node("/root/Map") and see if you still get the unknown character issue.

    | Make sure your indentation level is the same as the code around it. If you have been using tabs, then make sure it has the same tab indentation. If you are using spaces, then make sure it has the same amount of spaces as the code around it.

    | Make sure the code is inside a function. You can make a global variable (one outside of your functions) and assign it in _ready, but get_node does not work outside of a function (to the best of my knowledge).

    Hopefully one of those will solve the issue. :smile:

    The issue shouldn't be your singleton path, because I've used this code:
    get_node("/root/Globals").load_new_scene(bonus_level_scene)
    And it works fine (my singleton is named Global).

  • JohynJohyn Posts: 48Member
    edited April 7

    Sry can't get it... Red the doc, but that doesn't tell me what should I write on my particular singleton...
    Where should I put that first script before changing scenes? And it tells me the error that I should use onready var singleton = get_node(...)' instead. but then I got an unexpected token: identifier: singleton... :s

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    Here's what I would write (on your singleton script):

    extends Node
    var player_map_position = null

    Then in your project settings, load the script and give it a name. I think you said you did this, but I'm just mentioning it again just to be sure.

    Next, in your player script (or in the mini-map), add this:

    "Other stuff..."
    var singleton = null
    func _ready():
        "Other stuff..."
        singleton = get_node("/root/Map")
        if singleton.player_map_position != null:
            " Now we need to change the player's position. You may need to change the node path "
            $Map/Sprite.global_position = singleton.player_map_position

    (I'm assuming Map is the name of your singleton, and that this is added to the script you posted above)

    Then, whenever you change scenes, you just add this:

    "Other stuff, if there is any"
    "You may need to change the node path to the player depending on which scripts changes scenes"
    get_node("/root/Map").player_map_position = $Map/Sprite.global_position
    "Then change scenes. Example:"
    get_tree.change_scene("res://ExampleScenePath.tscn")

    And that will set the player_map_position variable right before you change. Because you check in _ready, when you return to a scene with the mini-map, the player will return to the position in the mini-map they were last in.


    Let me know if something does not make sense, and I'll do my best to explain.
    (It's starting to get late here, so it might be tomorrow when I can reply)

  • JohynJohyn Posts: 48Member
    edited April 8

    Ok, scripted the singleton, the player, but with the collisionshape2D changing scene, Map/Sprite cannot be found. It's the parent of my area2D, and I cannot get it out, as it stick to the map. A way to write a path from child to parents?

    Here's a shot from my tree:

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    I know of a couple ways:

    The first way is exporting a NodePath. You just need to add export (NodePath) var path_to_player with your global variables, and then when you need to get that node you use get_node(path_to_player). Then you just need to set the NodePath in the editor for every object that contains that script.

    The other way is by using get_parent as many times as you need to traverse up the scene tree. If you script is in the CollisionPolygon nodes, then you could get the player like this: get_parent().get_parent().get_parent().get_node("Player")

    I would suggest using the exported NodePath because it gives you the flexibility to place your script anywhere in the scene tree. It does mean you have to select the node in the editor for every object with that script, so it may be a little more time consuming up front, but it has the huge advantage of not needing your scene tree to be hard coded a certain way.

  • JohynJohyn Posts: 48Member

    Well, as I don't know how to set the NodePath, I tried the get_parent, but I got that error with the script of my collisionshape2D: invalid get index 'global_position' (on base: 'node( global.gd)')...
    Here's the script: Collsisionshape2D is the right one for that script to the singleton, right? Just before changing scene from map to zone.

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    You need it like this:

    func _on_Area2D_body_entered(body):
        get_node(“/root/Map”).player_map_position = get_parent().get_parent().get_parent().get_node(“Player”).global_position
        get_tree().change_scene(“res://Scenes/Pathvillage.tscn”)

    (Seems the code formatted strangely...)
    You just need to add another get_parent so you’re accessing Map in your scene. Then changing get_node(“/root/Map”) to get_node(“Player”) instead of get_node(“/root/Map”) should (hopefully) make it work.

    CollisionShape2D is the right one for that script to the singleton, right?

    Yup!


    And if you want to use a NodePath, it should look like this:
    (I think, I’m writing from memory... but I think the code below should work)

    Extends CollisionShape2D
    export (NodePath) var path_to_player
    func _on_Area2D_body_entered(body):
        get_node(“/root/Map”).player_map_position = get_node(path_to_player).global_position
        get_tree().change_scene(“res://Scenes/Pathvillage.tscn”)
  • JohynJohyn Posts: 48Member

    Tried with Player or Map, and still can't find it. Put KinematicBody2D or Sprite, but then I got this error:

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    Well, the error is saying it cannot convert an object (a variable) to a NodePath (either a NodePath object or a String). In other words, whatever you are passing into get_node is not a string or a NodePath, so it cannot get a node from it.

    Are you putting in ”Player” or Player? You have to add the otherwise it will think it’s a variable.

    Maybe remove one of the get_parent calls?

    Maybe print the node (just add print(<insert however many get_parent calls and/or the NodePath variable here>) and see what that prints out?

  • JohynJohyn Posts: 48Member
    edited April 8

    Ok, I find them with the assistance of the script, but now, when exiting my out of map zone, I'm returning to the change scene position, and I'm reloading my out of map zone...
    Btw, you got a good internal support when getting node eh. After your first bracket, there's the list of available nodes. :)

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    I'm confused as to what the problem is now...

    (I'm glad you got the nodes though! I really like using get_node over $ because I find the list is more reliable using get_node, though it's just a matter of personal preference)


    Is the problem now when you enter a scene the player starts at the wrong position? If that is the case, then you need to restructure how you are storing the player's position in your singleton. I would suggest having the exits set the singleton's position to the entrance/position they correspond to in the new map.You'll likely have to hard code the values in your collision shape, or used an exported Vector2 in your collision shape (Like this: export (Vector2) var scene_position)


    If the problem is when you return to the scene with the mini-map you instantly warp back, then it's because of how we are storing the value. This bug occurs because we are storing the player's position when they collide with the collision shape, so when we return to that scene we instantly send a collision signal and then change scenes again.

    There's several ways to fix this, but I'd go for the easiest route: Make a Node2D child in your collision shapes and instead of setting the position in the singleton using the player's position, you instead set the position in the singleton using the newly created Node2D. That will make when you return to that scene, you will return to that position (the Node2D) and should no longer warp from place to place, as long as your Node2D is not too close to your collision shape.


    Hopefully one of those answers you question! If it doesn't, let me know and we'll figure it out!

  • JohynJohyn Posts: 48Member

    :) Thxs for your patience!

    The problem is still the same, but, if it's all about `get_node("Map/Sprite").global_position = singleton.player_map_position``, isn't the position always the same, be it from Player or Node 2D, in the collision? As again, having changed, got again that back warp...

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    Thxs for your patience!

    No problem! It's my pleasure.
    (I've found helping others helps refine and/or add to my knowledge of Godot, so it's a win-win for everyone when a solution is found. Not to mention I just like helping others :smile: )


    I've attached a simple test project that should do what you want. Hopefully it will help. I added comments explaining what everything does, so hopefully it's easy to follow :smile:

    I realized while I was working through it that I actually misled you a little: You don't want to save the player's position when they've entered the Area2D because then when they return they will instantly be teleported again. Instead we want to store the position we want the player to start at in the next scene.

    In other words: Instead of setting the position the player was in when they collided with the Area2D, we instead set the position we want the player to be in when they enter the new scene. As long as this is done on both entrances/exits, then changing scenes is (mostly) seamless.

  • JohynJohyn Posts: 48Member

    :D I'm so thanksful for your time and work! I can just wish you'll get the very best of it then! :)

    But there are new pieces to fit now: I recreated your project with my setings, and yes, it's much smoother and simpler, though I can't come back to the map, geting that error:

    You have to know that my out of map scenes are no more following a player/sprite, but 'point and click' backgrounds. But I guess the change_scene is the same.

    Also, I'm limited in my movements by the frame, my map bein much bigger than the viewport. And more importantly, that's the player moving, and that means that I can't keep it in the midle of my 'minimap'...

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    Hey, sorry about the delay (was busy with other stuff).

    geting that error: Invalid get index 'player_map_position' (on base: 'Node (Global.gd)')

    What line of code is causing this problem? Also, did you change the variable name to player_map_position in Global.gd, or is it player_position?

    Also, I'm limited in my movements by the frame, my map bein much bigger than the viewport. And more importantly, that's the player moving, and that means that I can't keep it in the midle of my 'minimap'...

    It should work with a moving Camera2D, since get_global_mouse_position returns the position of the map in world space, not screen space. I added a camera to each of the player nodes in the example project and it works fine for me, so I think you can just use a Camera2D like normal (I'm assuming that is what you were using before)

  • JohynJohyn Posts: 48Member

    Told you that it's a no time pressures project, specially about help, eh. :)

    So yes, t'was the confusion player_map_position/player_position, but that being solved, I'm still going back to the same position in my map, when it's not the warp effect. That's strange, one area2D brings me back to the same position, and another brings me back there the first time, then warps back to the scene...

    The camera allows me a better range, but I'm still moving the player, and I cannot pin it in the center of my 'hole'(inside an image/sprite covering the map) for minimap... Tried to inverse map and player sprites, but that's buggy, and it's still not pined...

    So take your time to think about that, I've got plenty of others things to work on with that project, no hurry eh! :)

  • JohynJohyn Posts: 48Member

    Ok, sub added my 'holed' background and a camera with no drag margins to my player, and my minimap works fine at present. Just to find that remaining position now...

  • JohynJohyn Posts: 48Member
    edited April 13

    Ok, thought about givin a predefined position in my map for the player, when comin back to it from the 'zones'. A position set close to the zones, wich is not the best, but it should be easier. Here's the code of my zone node2D for that:

    `extends Node2D
    
    
    export (String, FILE) var new_scene_path = null
    export (Vector2) var new_scene_player_start_pos = null
    
    
    func ready():
        set_process_input(true) 
    
    
    
    func _input(event):
        set_process_input(true) 
        if event is InputEventMouseButton:
            if event.get_button_index() == BUTTON_RIGHT and event.is_pressed():
                                    get_node("/root/Global").player_map_position = Vector2(-497,-164)
                                    get_tree().change_scene("res://Scenes/TESTMAP.tscn")`
    

    But that doesn't work unfortunately... I'm still goin back to my initial position... An idea why so, if got the time?

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    The script above is on your player/thing-you-are-moving?

    If so, I think the reason it's not changing positions is because you're not changing positions in _ready. This code should work:

    func _ready():
        set_process_input(true)
        var global = get_node("/root/Global")
        if (global.player_map_position != null):
            self.global_position = global.player_map_position
            "Optional. Will make it where you start at the initial position"
            "if you change scenes without setting player_map_position"
            global.player_map_position = null

    You just need to add the code above to your player, and then it should start at the correct position (hopefully). :smile:

  • JohynJohyn Posts: 48Member

    No, actually it's in the node of my zone before changin scene back to the map, tryin to set the global singleton.Sry.

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    No, actually it's in the node of my zone before changin scene back to the map, tryin to set the global singleton.Sry.

    No worries! I just was not sure.

    Maybe in the player's _ready function print out the position to see if it's changing and/or being set? My hunch is something somewhere is not being set, or it's not being read in correctly.

  • JohynJohyn Posts: 48Member

    The player's position is back to theinitial one, with no concern for my get_node("/root/Global").player_map_position = Vector2(-497,-164) that should set it on my map when exitin the zone...

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin

    Hmm, strange. Can you post a picture/the-code from your player for debugging?

  • JohynJohyn Posts: 48Member
    edited April 15

    Here's the script of my player:

    extends KinematicBody2D
    
    var left_mouse_down = false
    var tween_node = null
    
    func _ready():
        tween_node = get_node("Tween")
    
    func _physics_process(delta):
        if Input.is_mouse_button_pressed(1):
            if left_mouse_down == false:
                var mouse_pos = get_global_mouse_position()
                tween_node.interpolate_property(self, "global_position", global_position, mouse_pos, 1, Tween.TRANS_LINEAR, Tween.EASE_IN)
                tween_node.start()
            left_mouse_down = true
        else:
            left_mouse_down = false
    

    Maybe lackin stuff, as I was thinkin that all I had to do was setin the singleton player_map_position when exitin the zone to the player map. Wich would set it for the player once in the map. But the position is not taken into account...

  • TwistedTwiglegTwistedTwigleg Posts: 701Admin
    Answer ✓

    Well, the reason the player isn't taking the position into account is because your not using it in the player. Thankfully it should be an easy fix!

    extends KinematicBody2D
    var left_mouse_down = false
    var tween_node = null
    ""
    ""
    func _ready():
        tween_node = get_node("Tween")
        var global = get_node("/root/Global")
        if (global.player_map_position != null):
            self.global_position = global.player_map_position
    ""
    ""
    func _physics_process(delta):
        if Input.is_mouse_button_pressed(1):
            if left_mouse_down == false:
                var mouse_pos = get_global_mouse_position()
                tween_node.interpolate_property(self, "global_position", global_position, mouse_pos, 1, Tween.TRANS_LINEAR, Tween.EASE_IN)
                tween_node.start()
            left_mouse_down = true
        else:
            left_mouse_down = false
    

    (I added some empty strings, "", to make the code easier to read on the forums)

    I just added a few lines of code to _ready so it gets the global singleton and sets the position if it has one. That should hopefully make the player take the position into account.

  • JohynJohyn Posts: 48Member

    Finally, we made it! Thxs so much! :)

Sign In or Register to comment.