|
|
|
|
Reply From: |
njamster |
Create an AutoLoad holding a variable player_position
defaulting to null. I’ll simply call this AutoLoad Global
in the following. Use an Area2D with a CollisionShape2D to create a door-scene and then add the following script to it:
extends Area2D
export (PackedScene) var change_to_scene
export (Vector2) var move_to_position
func _on_Area2D_body_entered(body):
if change_to_scene and move_to_position:
Global.player_position = move_to_position
get_tree().change_scene_to(change_to_scene)
Make sure the function _on_Area2D_body_entered
is properly connected to the body_entered
-signal of the Area2D and that your player is a body, not an area.
Finally add the following bit to the scripts of all your level scenes:
func _ready():
if Global.player_position:
get_node("Player").global_position = Global.player_position
That’s assuming that your player-node is called “Player” and is a direct child of each level scene. If not, you need to adapt the node-path used here accordingly.
Now you can add a new instance of the door-scene to any of your levels and once both export variables are set, the door will start working as intended. This can be easily adapted to create “teleporting” areas inside the same map as well:
func _on_Area2D_body_entered(body):
if change_to_scene:
Global.player_position = move_to_position
get_tree().change_scene_to(change_to_scene)
else:
var player_node = get_parent().get_node("Player")
player_node.global_position = move_to_position
Thank you for answering But I still can’t seem to get it to work quite right. I either get an error ( previous node freed) or my player keeps spawning in the middle of the map when leaving the house instead of spawning in front of the door like he should. Maybe I’m missing something obvious or maybe a little more information is needed ¯_(ツ)_/¯
my player is already in both scenes, so there’s no need to instance him over though I know how to do it both ways. I’m using an area 2D for tile base movement so there’s no kinematic body to use. Here’s some code
my door code:
extends Area2D
export(String, FILE, "*.tscn") var world_exit
func _process(_delta):
var areas = get_overlapping_areas()
for area in areas:
if area.name == "Player" && Input.is_action_just_pressed("ui_accept"):
get_tree().change_scene(world_exit)
func _on_Door_area_entered(_area: Area2D) -> void:
# Store value
I’m thinking I can store/give a value once the player overlaps with the door area. And I can use that variable to set a new spawn location. Just have no idea how to do that.
code attached to the main level node:
extends Node2D
func _ready():
$Player.global_position = $Player_spawner.global_position
the Player spawner will be a position 2D so I can set them freely throughout the world and not have to worry about a door or whatever being put in the middle of a forest. Woulds just seem out of place.
I also have a singleton as well, just have no idea what to do with it.
code is literally just var playerpos = null
maybe I can use this to store the values I need but I’m not really sure.
If you have any other ways to make this work I’m all ears I’ll just fiddle around with your code and mines until it works like I want it to. Hope what I’ve posted isn’t to confusing. Thank you again
RakuNana | 2020-07-24 05:18
I got it to work! Thank you brother! So for anyone else trying to get this to work here’s my code.
code for the door/portal:
extends Area2D
export(String, FILE, "*.tscn") var world_exit
export (Vector2) var spawn_pos
func _process(_delta):
var areas = get_overlapping_areas()
for area in areas:
if area.name == "Player" && Input.is_action_just_pressed("ui_accept"):
get_tree().change_scene(world_exit)
Global.player_pos = spawn_pos
func _on_Door_area_entered(_area: Area2D) -> void:
if world_exit:
get_node("/root/Global").player_pos = spawn_pos
code attached to my main level node:
extends Node2D
func _ready():
if Global.player_pos:
get_node("Player").global_position = Global.player_pos
my autoload/singleton
extends Node
var player_pos = null
when setting the spawn pos variables be sure they match your new scene not your current one. IE portal in level one should have the spawn pos for level two and vice versa. You’ll have to set each spawn point manually this way but, this does in fact work. I’ll be experimenting to see if I can automate the process, so all you’d have to do is instance a new door/portal and the spawn coordinate will set automatically. Cheers!
RakuNana | 2020-07-24 16:31
Hey there! This post couldn’t have had better timing. I’m also looking to do exactly what you did, and following this exchange helped me get there. I’m now facing a new problem that I’m wondering if you’ve dealt with yet.
Whenever my player loads into a new scene, it uses the left idle sprite, really I’d want them to continue their animation from the previous scene. Right now, if ui_up is active as my character travels between scenes, there will be one frame of the left idle sprite that appears as they spawn in the new scene, before continuing to walk up.
I’m wondering if there’s a way to use the same process of getting global information to change the current character sprite animation upon loading a new scene. Another approach would be to keep my input vector consistent between scenes.
I figured you would be dealing with this too, or else you’ve already prevented this issue from happening. If you have any ideas, I’d appreciate the help!
SamACSmith | 2020-07-26 16:56
I’m wondering if there’s a way to use the same process of getting global information to change the current character sprite animation upon loading a new scene.
Yeah, the approach is pretty much the same: Store the properties you’re interested in (judging from your description: animation
and frame
) in an AutoLoad and apply them when the new scene is getting ready. Something like this:
func _ready():
var player = load("<PathToPlayerScene>").instance()
player.get_node("AnimatedSprite").animation = Global.player_animation
player.get_node("AnimatedSprite").frame = Global.player_animation_frame
add_child(player)
njamster | 2020-07-26 20:36
Thanks for responding! I’m self-taught with coding and fairly new at Godot so I appreciate it!
I’m still having some issues though since I’m using an AnimationPlayer and an AnimationTree instead of an AnimatedSprite. The way my movement is coded in the Player script, the player takes an input vector from the ui. If input_vector is (0,-1) the player goes up, (-1,0) they go left, etc. This also tells the AnimationTree to play the correct directional animation.
I’m trying to make a Vector2 in an Autoload that tells the Player script what the input_vector is upon entering a new scene. This seems simpler than telling it to export properties of the AnimationPlayer or the AnimationTree. I’ve been having problems accessing this variable from other scenes though. Do you have any advice? Thanks again for responding. Here is my Player script for reference:
extends KinematicBody2D
const ACCELERATION = 1000
const MAX_SPEED = 70
const FRICTION = 1000
var velocity = Vector2.ZERO
onready var animationPlayer = $AnimationPlayer
onready var animationTree = $AnimationTree
onready var animationState = animationTree.get("parameters/playback")
func _process(delta):
var input_vector = Vector2.ZERO
input_vector.x = Input.get_action_strength("ui_right") -
Input.get_action_strength("ui_left")
input_vector.y = Input.get_action_strength("ui_down") -
Input.get_action_strength("ui_up")
input_vector = input_vector.normalized()
if input_vector != Vector2.ZERO:
animationTree.set("parameters/Idle/blend_position", input_vector)
animationTree.set("parameters/Run/blend_position", input_vector)
animationState.travel("Run")
velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
else:
animationState.travel("Idle")
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
velocity = move_and_slide(velocity)
SamACSmith | 2020-07-27 17:11
Well, then before you change the scene, get the player node and store the input_vector
:
Global.last_input_vector = get_node("Player").input_vector
get_tree().change_scene("<PathToNextScene>")
And then have the player load that value from the AutoLoad:
func _ready():
input_vector = Global.last_input_vector
However, looking at your code input_vector
(a) isn’t a global variable and only known in the scope of the _process
-method and (b) redefined each frame based on the player input. If it can change each frame, there is no point in preserving it.
In case that doesn’t solve your problem, I recommend you create a separate question for your problem. This way a lot more people will see it and might help you.
njamster | 2020-07-27 20:07
Will do! Thanks again for clearing this up!
SamACSmith | 2020-07-28 17:28