Make a Stop Time Ability like Zawarudo

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By hearto
:warning: Old Version Published before Godot 3 was released.

Hello,

I Want to achieve an effect on my shmup like the Zawarudo from Jojo Series https://www.youtube.com/watch?v=B9za3TrVT3g

Basically:

  • Player ship could move
  • Player bullets spawns but wait for effect end to start moving
  • Enemies and his bullets stay frozen
  • Player could collide and die from frozen enemies

So I wan’t a way to pause the whole game with exception of player and make the possibility to still collide with another objects.
I know the existence of pause behavior for PAUSE the game, but it’s seems like a hacky solution for my problem, also the game has a pause so with using pause to achieve effect could interfere with the normal pause of the game.

So you guys know a clean way to achieve my requirements?

Maybe I could replicate the pause behavior from source code and make like layers of pause as a new feature?
For example:
Pause layer 1: here is the normal pause behavior of pause/resume the whole game.
Pause layer 2: the whole game with exception of player for stop time behaviour.
Pause layer 3: pause for certain enemies/player/bullets for animations.
So the pause layers are like collision mask layers idea, i could pause specific layers and all the nodes on that layer are paused.

:bust_in_silhouette: Reply From: tiernich

already try set the Pause Mode to Process?

see: pausing games

Yes, but with that I have conflict with the normal pause of the game, IE: the user uses the Stop Time, and then press the pause button for pause the game.
See my idea for achieve what I want which is a modification of the engine for support multiple layers of pause.
Also take a note on the requirements the player collision with enemy objects.

hearto | 2016-03-26 22:34

oh, ok. sorry for the weak answer, dont understand english very well(yet) and some times is hard to pickup the real mean of the hole text and i understand wrong or miss something. haha :smiley:

tiernich | 2016-03-27 03:14

:bust_in_silhouette: Reply From: The_Duskitty

You could always try setting the speed of things to 0 if you have moving objects and pause the animations, not sure if it would work too well on physics based objects seeing as they’d need time to come to a complete stop

That is one of my options but is not the cleanest one, because it implies manage of the pause for EACH object, example enemy A has 2 animation players, enemy B has 1 and another subnode which need pause on certain part of code (fixed_update skip for example). That could lead to problems, bugs and heavy testing for each enemy/bullet.

hearto | 2016-03-27 01:17

:bust_in_silhouette: Reply From: zendorf

I have a freeze effect going on in my game, so that when my player dies the camera zooms in on it, but everything is frozen around him, including bullets, explosions, etc. Basically a matrix style bullet effect.

The way I have done it is to organise all my code into functions and then then check if a global flag has been triggered before any movement. This does require extra legwork on every moving object, but it does give you nice clean code to work with.

An example…my global bool flag is “freeze”, so the object code will function normally until the point where the global flag is triggered.

func _fixed_process(delta):    
    if !freeze:                 # keep going until freeze flag is triggered
        move_player(delta)
        check_collisions(delta)
        update_gui(delta)

Another option that I tried was to have a global “time_scale” variable, which has the advantage of being able to slow down or speed up gameplay on some or all objects. Just multiply the time_scale parameter to the velocity of all objects. 0.5 for halfspeed 2 for doublespeed, 0 for freeze, etc.

Example:

velocity = Vector3(0, 10, 0)
move(velocity * delta * time_scale)

That could be my another option, Should be more friendly if godot lets me extend all of my node types from one base class and expose the option, but I couldn’t figure how to do that without losing the different base classes (example extends from RigidBody and My base class)

hearto | 2016-03-27 01:46

Time scale is a good idea, doing something like “delta *= time_scale” at the top of the _process would be simpler. Then for RigidBodies, I think you can put them to sleep with set_sleeping(true) in _fixed_process as well and that should stop them too. And for the player not to be slowed, their time scale can remain 1.0.

batmanasb | 2016-03-27 02:17

:bust_in_silhouette: Reply From: hearto

Ok, Update on this I finished my stop time:
https://gfycat.com/WhirlwindAcrobaticGuanaco
So I wanna to share my approach.
Basically is based on the solution proposed by zendorf of a global var with quality of life modifications.

First you make a node with a script, on my case is local from stage nodes, not a global script with autoload.
On that class you put your var for frozen, on my case is a bool, but also you make this var a setget. Why is that, because you could emit a signal when the var is changed and notify all the registered objects, this solution work with animations for example and the fixed updates or collisions using the bool directly.

My code look like this:

extends Node
#When this var is set to true, all enemies and bullets should freeze with the exception of player
var zawarudo = false setget _set_zawarudo

signal zawarudo_start()
signal zawarudo_stop()

func _ready():
	pass

func _set_zawarudo(value):
	if(zawarudo != value):
		zawarudo = value
		if(zawarudo):
			emit_signal("zawarudo_start")
		else:
			emit_signal("zawarudo_stop")

And as an example I used it to stop an animation player here:

extends AnimationPlayer

#Class for pause animation players on zawarudo effect
var _zawarudo

func _ready():
	_zawarudo = get_tree().get_nodes_in_group("_zawarudo")[0]
	if(!_zawarudo.is_connected("zawarudo_start", self, "_zawarudo_start")):
		_zawarudo.connect("zawarudo_start", self, "_zawarudo_start")
	if(!_zawarudo.is_connected("zawarudo_stop", self, "_zawarudo_stop")):
		_zawarudo.connect("zawarudo_stop", self, "_zawarudo_stop")
	if(_zawarudo.zawarudo):
		_zawarudo_start()

func _zawarudo_start():
	if(is_active()):
		set_active(false)
	
func _zawarudo_stop():
	if(!is_active()):
		set_active(true)

Note I use a group for the access to the zawarudo class.