How to freeze sprite animation on last frame? - game-maker

I want to make a sprite in game maker that contains 5 sub images to stop when it reaches the last one how can I do this using code?

In Animation End event:
image_speed = 0;
image_index = image_number - 1;

Run this in the Step even of your object:
If image_index == 4 Then image_speed = 0;
image_index is the index of the current frame in the object's sprite's animation, image_speed is the speed of the animation (in frames per step). This code just checks if the animation is at the final frame (the frames are 0-indexed, so the 5th frame is index 4.), and if so sets the speed to 0.

Related

Grid base movement with gravity and step by step - Godot

I'm trying to make a grid-based movement game but when character jumps, it's affected by gravity; too, the character doesn't receive instructions by the keyboard, but it receive instructions step by step when the players push "GO" button
I have the gravity affecting the character, but I don't know how to move the character step by step on grid-based movement. Someone has an idea or some video tutorial
The var "moviendose" is changed to true when the player press GO
`
extends KinematicBody2D
var moviendose = false
var lista_habilidades_jugador = []
onready var tweene = $AnimatedSprite
const GRAVITY = 9.8
var velocity = Vector2.ZERO
const def_habilidades = {
"Habilidad1": Vector2(16,0),
"Habilidad2": Vector2(-16,0),
"Habilidad3": Vector2(0,(-GRAVITY*16))
}
func _physics_process(delta):
velocity.y += GRAVITY
if lista_habilidades_jugador.size() == 0:
moviendose = false
if moviendose == true:
movimiento()
velocity = move_and_slide(velocity)
func movimiento():
for mov in lista_habilidades_jugador:
if mov == "Habilidad1":
velocity = velocity.move_toward(def_habilidades[mov] , 5)
tweene.flip_h = false
tweene.play("correr")
if mov == "Habilidad2":
velocity = lerp(velocity + def_habilidades[mov], Vector2.ZERO,20)
tweene.flip_h = true
if mov == "Habilidad3":
velocity = velocity + def_habilidades[mov]
self.lista_habilidades_jugador.pop_front()
`
[bar of movement] (https://i.stack.imgur.com/cnwqA.png)
I'll break this into issues:
Units
Sequencing movement
Grid movement
Overshooting
Jump
Units
Presumably, this is an earth-like gravity:
const GRAVITY = 9.8
Which means this is an acceleration, in meters per second square.
But this is a 2D enviroment:
extends KinematicBody2D
So movement units are pixels, not meters. Thus the velocity here is in pixels per seconds:
velocity = move_and_slide(velocity)
And thus here you are adding meters per second squared to pixels per second, and that is not right:
velocity.y += GRAVITY
I bet it looks about right anyway, I'll get back to why.
Do you remember physics? Velocity is displacement over time (speed is distance over time). And we have that time, we call it delta. So this would be meters per second:
GRAVITY * delta
And if we add a conversion constant for pixels to meters, we have:
GRAVITY * delta * pixels_per_meter
How much is that constant? How much you want. Presumably something noticiable, you need to check the size of your sprites to come up with some pixel scaling that works for you.
That means the constant is big, but the time between frames we call detal is small, less than a second. So delta makes the value small and the pixel to meters constant makes it big. And I expect these effects to compensate each other, so it kind of looks good anyway… But having them 1. makes it correct, 2. makes it frame rate independent, 3. gives you control over the scaling.
The alternative is to come up with the gravity value in pixels per second squared. In other words, instead of figuring out a conversion constant from pixels to meters, tweak the gravity value until it works like you want (which is equivalent to having it pre-multiplied by the constant), and just keep delta:
GRAVITY * delta
Now, what is this?
velocity = velocity.move_toward(def_habilidades[mov] , 5)
We are changing the velocity towards some known value at steps of 5. That 5 is a delta velocity (delta meaning change here). You would want an acceleration to make this frame rate independent. Something like this:
velocity = velocity.move_toward(def_habilidades[mov] , 5 * delta)
Which would also imply to pass delta to the movement method.
And what the heck is this?
velocity = lerp(velocity + def_habilidades[mov], Vector2.ZERO,20)
I'll assume the value you are adding to the velocity is also a velocity. I'll challenge that later, it is not the point here.
The point is that you are using lerp weird. The lerp function is meant to give you a value between the first two arguments. The third arguments controls how close is the result towards the first or second argument. When the third argument is zero, you get the first argument as result. And when the third argument is one, you get the second argument. But the third argument is 20?
I don't know what the intention here is. Do you want to go to zero and overshoot?
I don't know. But as far as units are concerned, you are changing the velocity but not by a delta, but by a factor. This is not an acceleration, this is a jerk (look it up).
Sequencing movement
Here you have an unusual structure:
func movimiento(): # you probably will add delta as parameter here
for mov in lista_habilidades_jugador:
# PROCESS THE MOVEMENT
self.lista_habilidades_jugador.pop_front()
You iterate over a list, and each iteration - after processing the movement - you remove an element from the list. Yes, the same list.
If the list stars with three items:
The first iteration looks into the first element and removes the third. The list now has two elements.
The second iteration looks into the second element and removes the second. The list now has one element.
No third iteration.
Don't do that. In general you want to one element, and process it. The minimum would be like this:
func movimiento():
var mov = self.lista_habilidades_jugador.pop_front()
# PROCESS THE MOVEMENT
The next time you call the movement method it will pull the next element, and so on.
Ah, but you are calling the movement method each physics frame. Except presumably the motion should take more than one physics frame. So you should hold up until the current movement finishes.
This is how I would structure it:
func _physics_process(delta):
velocity.y += GRAVITY
movimiento()
velocity = move_and_slide(velocity)
Then the movement method can set the variable:
func movimiento():
# if there are no movements we do nothing
if lista_habilidades_jugador.size() == 0:
return
# get the first movement
var mov = self.lista_habilidades_jugador[0]
moviendose = true
# PROCESS THE MOVEMENT
if not moviendose: # we will come back to this
# we are done moving
self.lista_habilidades_jugador.pop_front()
Grid movement
To process each movement we need to update the velocity and know when it reached the destination. We can't do that if we just have a velocity.
In general we don't want to define velocities for grid movement. But displacements. Which will be an issue for the jump but we will get to that.
Thus, I'll assume that the values you have in your dictionary are not velocities but displacements. In fact, we are going to store a target:
var target := Vector2.ZERO
So we can keep track to were we have to move towards. And we need to update that when we pull a new movement from the list:
func movimiento():
# if there are no movements we do nothing
if lista_habilidades_jugador.size() == 0:
return
# get the first movement
var mov = self.lista_habilidades_jugador[0]
if not moviendose:
# just starting a new movement
var displacement = def_habilidades[mov]
target = position + displacement
moviendose = true
# PROCESS THE MOVEMENT
if not moviendose: # we will come back to this
# we are done moving
self.lista_habilidades_jugador.pop_front()
The other thing we need is how long it should take to move. I'll come up with some value, you tweak it as you see fit:
var step_time := 0.5
Do you remember physics (again)? the velocity is displacement over time.
velocity = displacement / step_time
So:
func movimiento():
# if there are no movements we do nothing
if lista_habilidades_jugador.size() == 0:
return
# get the first movement
var mov = self.lista_habilidades_jugador[0]
if not moviendose:
# just starting a new movement
var displacement = def_habilidades[mov]
velocity = displacement / step_time
target = position + displacement
moviendose = true
# PROCESS THE MOVEMENT
if not moviendose: # we will come back to this
# we are done moving
self.lista_habilidades_jugador.pop_front()
And what is left is finding out if we reached the target. A first approximation is this:
func movimiento():
# if there are no movements we do nothing
if lista_habilidades_jugador.size() == 0:
return
# get the first movement
var mov = self.lista_habilidades_jugador[0]
if not moviendose:
# just starting a new movement
var displacement = def_habilidades[mov]
velocity = displacement / step_time
target = position + displacement
moviendose = position.distance_to(target) > 0
if not moviendose:
# we are done moving
self.lista_habilidades_jugador.pop_front()
And we are done. Right? RIGHT?
Overshooting
We are not done. We move some distance each frame, so the distance to the target will likely not hit zero. And that is without talking about floating point errors.
Thus, instead of finding out if we reached the target, we will find out if we will overshoot the target. And to do that we need to compute how we will move. Start by bringing delta in (I'm also adding type information):
func _physics_process(delta:float) -> void:
velocity.y += GRAVITY
movimiento(delta)
velocity = move_and_slide(velocity)
func movimiento(delta:float) -> void:
# if there are no movements we do nothing
if lista_habilidades_jugador.size() == 0:
return
# get the first movement
var mov:String = self.lista_habilidades_jugador[0]
if not moviendose:
# just starting a new movement
var displacement:Vector2 = def_habilidades[mov]
velocity = displacement / step_time
target = position + displacement
moviendose = position.distance_to(target) > 0
if not moviendose:
# we are done moving
self.lista_habilidades_jugador.pop_front()
Now we can compute how much we will move this frame:
velocity.length() * delta
And compare that with the distance to the target:
moviendose = position.distance_to(target) > velocity.length() * delta
Now that we know we will overshoot, we should prevent it. The first idea is to snap to the target:
if not moviendose:
# we are done moving
position = target
self.lista_habilidades_jugador.pop_front()
For reference I'll also mention that we can compute the velocity to reach the target this frame (within floating point error):
if not moviendose:
# we are done moving
velocity = velocity.normalized() * position.distance_to(target) / delta
self.lista_habilidades_jugador.pop_front()
Here we normalize the velocity to get just its direction. And do you remember physics? Yes, yes. That is distance over time.
However we will not use this one, because it would mess with the jump…
Jump
But the jump does not work like that. You cannot define a jump the same way. There are six ways to define a vertical jump:
By Gravity and Time
By Gravity and Speed
By Gravity and Height
By Time and Speed
By Time and Height
By Speed and Height
Defining the jump by gravity and speed is very common, at least among beginners, because you already have a gravity and then you define some upward velocity and you have a jump.
It is, however, a good idea to define the gravity using height because then you know - by design - how high it can jump which can be useful for scenario design.
In fact, in the spirit of keeping the representation of movement by displacement, using a definition by gravity and height is convenient. So I'll go with that. Yet, we will still need to compute the velocity, which we will do like this:
half_jump_time = sqrt(-2.0 * max_height / gravity)
jump_time = half_jump_time * 2.0
vertical_speed = -gravity * half_jump_time
That comes from the equations of motion. I'll spare how to come up with that, feel free to look it up.
However, note that the vertical component will not be the destination vertical position, but the height of the jump. Which is half way through the jump. So for consistency sake, when you specify a jump the displacement will be to the highest point of the jump…
We will know it is a jump because it has a vertical component at all.
if not moviendose:
# just starting a new movement
var displacement:Vector2 = def_habilidades[mov]
target = position + displacement
if displacement.y == 0:
velocity = displacement / step_time
else:
var half_jump_time = sqrt(-2.0 * displacement.y / gravity)
var jump_time = half_jump_time * 2.0
velocity.y = -gravity * half_jump_time
velocity.x = displacement.x / half_jump_time
And that highlights another problem: We cannot say the motion ended when reached the target, we also need to check if the character is on the ground.
For that effect, I'll have one extra variable (add at the top of the file):
var target_reached := false
So we can say:
var distance:float = velocity.length() * delta
target_reached = target_reached or position.distance_to(target) > distance
moviendose = not (target_reached and is_on_floor())
if not moviendose:
# we are done moving
target_reached = false
position = target
self.lista_habilidades_jugador.pop_front()
Unless I forgot to include something, that should be it.
Addendum: Yes, I forgot something. When the character landed that is not the target position (because the target position is the mid point of the jump). You could, in theory, snap mid jump, but I don't think that is of much use. Instead consider snapping the position (or issuing a move) once landed to the nearest position of the grid. You can use the method snapped on the vector to find out where it aligns to the grid size.

Understanding Recursion with Merge sort

I see some of the posts to understand merge sort. I know recursive methods maintains stack to hold values. (my understand was return statement result will be in stack )
private int recur(int count) {
if (count > 0) {
System.out.println(count);
return count + recur(--count); // this value will be in stack.
}
return count;
}
I am confusing in merge sort how stack is maintaining here.
private void divide(int low, int high) {
System.out.println("Divide => Low: "+ low +" High: "+ high);
if (low < high) {
int middle = (low + high) / 2;
divide(low, middle); // {0,7},{0,3}, {0,1} ;
divide(middle + 1, high); // {0,0}; high = 1; // 2nd divide
combine(low, middle, high);
}
}
Is stack for all local variables?
When 2nd recursive method calls, 1st recursive will also join?
How stack are maintained in such cases?
You only have to know that a statement needs to finish and return and that you call divide or combine from divide works the same. Both need to finish before the next line of code can be executed or, if there are no more lines, the function returns. Yes, it's done with stack but it's really not important.
The state of the waiters variables low, high and middle is only the current invocations bindings so they don't get mixed with other invocations.
Every time you nest a new call it gets it's own variables and each need to finish. When the low-middle is finished it calls middle+1-high and when that finished combine. Those calls will do the same so you will have deeper nesting and how the call structure will be visited is like like a binary tree structure with the leafs being low == high (one element).
A word of advice. When looking at recursive code try doing it from leaf to more complex tree. eg. try it out with base case first, then the simplest of default case. eg.
1 element array: does nothing
2 element array: -> 1 element array (see 1.), 1 element array, combine
4 element array: -> 2 element array (see 2.), 2 element array, combine
Notice that the 2. you know both recursive calls won't do anything and combine will do perhaps a swap. The 3. does 2. twice (including the swap) before combine that will merge 2 2 element arrays that are sorted. You are perhaps looking at it the other way, which requires you to halt 3. to do 2. that halts it and does 1., then the next 1, then back to 2. to do the text that has two 1s... It needs pen and paper. Looking at it from leaf to root using what you have learned of it so far lets you understand it much easier. I do think functional recursion is easier to grasp than mutating structures like your merge sort. eg. fibonacci sequence.

QProgressDialog does not display immediately

I have a QProgressDialog that I want to display immediately
QProgressDialog *progress = new QProgressDialog("Downloading files...",
"Cancel", 0, 2*selection.size()+1);
progress->setMinimumDuration(0);
progress->setWindowModality(Qt::WindowModal);
progress->setValue(0);
Then I run a for loop with the task and finally assign the maximum value:
for (int i = 1; i < selection.size()+1; ++i)
{
progress->setValue(2*i-1);
if (progress->wasCanceled())
break;
do_half_task();
progress->setValue(2*i);
if (progress->wasCanceled())
break;
do_second_half();
}
progress->setValue(2*selection.size()+1);
But with this code, the dialog window borders appear, transparents without any widgets inside, and only gets filled with the label and progressbar when a full for loop has completed.
I think this is because only after a full loop has completed is that Qt can compute the duration of each step, and check that it will be >0 which I am setting as minimum duration. However, from the docs I see
minimumDuration : int
If set to 0, the dialog is always shown as soon as any progress is set. The default is 4000 milliseconds.
From where I would've expected the dialog to show up immediately in the first loop pass after setting progress->setValue(1).
How can I get my QProgressDialog to appear immediately?
Not sure if this matches for Qt too
but in C# if you run the execution of your code in the same thread like
ProcessBar p = new ProcessBar();
this.Controls.Add(p);
...
for (int i = 0; i < 100; i++) {
p.Value = i;
Thread.Sleep(1);
}
then you have the problem that your Form does not get to the code where it is redrawn.
Maybe try making your execution loop in a nother thread?
When you make the main thread go into a loop, it can't do any event processing until the loop ends and your method returns.
So it can only process all "paint update" requests once you are done.
You can call QCoreApplication::processEvents() inside the loop to allow it to return to event processing for a while.
To show the dialog immediately then just call QProgressDialog::show().

how to rewrite the recursive solution to iterative one

The problem is derive from OJ.
The description is :
We are playing the Guess Game. The game is as follows:
I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I'll tell you whether the number I picked is higher or lower.
However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked.
Given a particular n ≥ 1, find out how much money you need to have to guarantee a win.
I write small snippet about MinMax problem in recursion. But it is slow and I want to rewrite it in a iterative way. Could anyone help with that and give me the idea about how you convert the recursive solution to iterative one? Any idea is appreciated. The code is showed below:
public int getMoneyAmount(int n) {
int[][] dp = new int[n + 1][n + 1];
for(int i = 0; i < dp.length; i++)
Arrays.fill(dp[i], -1);
return solve(dp, 1, n);
}
private int solve(int[][] dp, int left, int right){
if(left >= right){
return 0;
}
if(dp[left][right] != -1){
return dp[left][right];
}
dp[left][right] = Integer.MAX_VALUE;
for(int i = left; i <= right; i++){
dp[left][right] = Math.min(dp[left][right], i + Math.max(solve(dp, left, i - 1),solve(dp, i + 1, right)));
}
return dp[left][right];
}
In general, you convert using some focused concepts:
Replace the recursion with a while loop -- or a for loop, if you can pre-determine how many iterations you need (which you can do in this case).
Within the loop, check for the recursion's termination conditions; when you hit one of those, skip the rest of the loop.
Maintain local variables to replace the parameters and return value.
The loop termination is completion of the entire problem. In your case, this would be filling out the entire dp array.
The loop body consists of the computations that are currently in your recursion step: preparing the arguments for the recursive call.
Your general approach is to step through a nested (2-D) loop to fill out your array, starting from the simplest cases (left = right) and working your way to the far corner (left = 1, right = n). Note that your main diagonal is 0 (initialize that before you get into the loop), and your lower triangle is unused (don't even bother to initialize it).
For the loop body, you should be able to derive how to fill in each succeeding diagonal (one element shorter in each iteration) from the one you just did. That assignment statement is the body. In this case, you don't need the recursion termination conditions: the one that returns 0 is what you cover in initialization; the other you never hit, controlling left and right with your loop indices.
Are these enough hints to get you moving?

Game Maker: create an ennemy at a random position on the ground

I know a bit about coding (in Python, C and XHTML) and I'm trying to understand the basics of Game Maker. I have created a room with enemies moving, colliding to the walls and all, but now, I'd like to randomly spawn enemies in the room as long as they are on a ground. For now, it only works when I spawn them randomly.
Here is the code I put in the Create event of the obj_enemy but obviously something isn't working since it does not spawn any enemy at all.
Also, don't know if it matters, but if I haven't already placed myself an obj_enemy in the room, they do not spawn neither...
// INIT //
dir = -1; // direction
movespeed = 3; // movement speed
hsp = 0; // horizontal speed
vsp = 0; // vertical speed
grav = 0.5; // gravity
// CREATE //
// Find a random X position in the room
var randx = random(room_width);
// Find a random Y position in the room
var randy = random(room_height);
// If the random position is empty
if position_empty (randx, randy)
{
// If there is a block
// 16 pixels under
// the random Y position
// (the sprite of obj_enemy is 32x32 pixels)
if place_meeting (randx, randy+16, obj_block01)
{
// If there is less than 4 ennemies
if instance_number (obj_ennemy) <= 4
{
// Create an ennemy
instance_create(randx, randy, obj_ennemy);
}
}
}
This is the create event of obj_enemy. if there are no obj_enemy's in the room then this code will never get run!
You either need to start with at least one enemy in the room or create a controller object in charge of creating enemies that you put into the room instead (I recommend this approach).
Also even if the code does get run then the chances of spawning an enemy just over a wall in the correct location is quite small so you will have to run the program many times before you see it happen. To avoid this just put the spawn code into a while true loop and break from it once 4 enemies have been spawned:
while (instance_number (obj_ennemy) <= 4)
{
// Find a random X position in the room
var randx = random(room_width);
// Find a random Y position in the room
var randy = random(room_height);
// If the random position is empty
if position_empty (randx, randy)
{
// If there is a block
// 16 pixels under
// the random Y position
// (the sprite of obj_enemy is 32x32 pixels)
if place_meeting (randx, randy+16, obj_block01)
{
// Create an ennemy
instance_create(randx, randy, obj_ennemy);
}
}
}

Resources