Jumping only when the hero is above the platform - Spritekit game - xcode

Hi everyone For the pass two days I was trying to solve the problem I'm having without success..
I want to make game when the hero jumps on platform and goes up. It is Doodle Jump type of a game.
Here is the code
I make the the world node
SKNode *world;
Then the hero
SKSpriteNode *hero;
// setting the physicsBody
// add categoryBitMask = heroCat;
// add contactTestBitMask = platformCat;
// add collisionBitMask = 0;
The platform
SKSpriteNode *platform;
// setting the physicsBody
// add categoryBitMask = platformCat;
// add collisionBitMask = 0;
In didBeginContact
// Only bounce the hero if he's falling
if (hero.physicsBody.velocity.dy < 0){
hero.physicsBody.velocity = CGVectorMake(hero.physicsBody.velocity.dx, 500.0f);
}
In didSimulatePhysics
// if the game is started and the hero position is > 0 and hero position > the new max hero y
if(self.isStarted && hero.position.y > 0 && hero.position.y > _maxHeroY){
CGPoint positionInScene = [self convertPoint:hero.position fromNode:hero.parent];
world.position = CGPointMake(world.position.x, world.position.y -positionInScene.y);
}
So far so good but what the I notice that sometimes I can jump when:
in didSimulatePhysics i also gen new platform on random Y with fixed x value from the preveos platform
when the hero goes up the world position change
the hero is falling
he is not above the platform but abble to contact with his body, this is wrong cause he has to jump only when the feet are above the platform
So I change the didBeginContact
// hero position in the world
CGPoint positionInScene = [self convertPoint:hero.position fromNode:node.parent];
// platform position in the world
CGPoint positionInSceneP = [self convertPoint:node.position fromNode:node.parent];
// get the positon of the bottom edge of the hero (the feet)
int heroBottomEdge = positionInScene - hero.frame.size.height/2;
// get the positon of the top edge of the platfrom
int platformTopEdge = positionInScene - node.frame.size.height/2;
// Only bounce the hero if he's falling and the bottom edge of the hero node(the feet) is above the platform top edge or or equal to jump
if (hero.physicsBody.velocity.dy < 0 && heroBottomEdge >= platformTopEdge){
hero.physicsBody.velocity = CGVectorMake(hero.physicsBody.velocity.dx, 500.0f);
}
The problem is that sometimes platformTopEdge is bigger that the heroBottomEdge and ther hero pass through the platform without jumping
I also try with just hero.position.y and node.position.y but the effect is the same

Related

Godot jumping animation just plays the first frame

I have a script in my simple platformer game which says that if my player is in the ground and "Z" is pressed, his movement in the Y axis is going to go up to 600, and if he's not in the ground, he's going to perform the jump animation.
So here's the thing, I know it plays only the first frame of the jumping animation because the code is constantly detecting that the player is in the air. I want a way to tell the code to trigger the animation only once.
I tried using a function called input_(event): but it seems that it doesn't have a is_action_just_pressed type of Input, just is_action_pressed.
I'm fairly new to Godot and don't know how to use signals. Signals might help via animation_finished(), although that function might have nothing to do with what I actually want to do in my code.
Here's my code:
extends KinematicBody2D
#Variables van aquí
var movimiento = Vector2();
var gravedad = 20;
var arriba = Vector2(0, -1);
var velocidadMax = 600;
var fuerza_salto = -600;
var aceleracion = 5;
var saltando = false;
func _ready(): # Esto es void Start()
pass;
func _physics_process(delta): #Esto es void Update()
movimiento.y += gravedad;
if Input.is_action_pressed("ui_right"):
$SonicSprite.flip_h = true;
$SonicSprite.play("Walk");
movimiento.x = min(movimiento.x +aceleracion, velocidadMax);
elif Input.is_action_pressed("ui_left"):
$SonicSprite.flip_h = false;
$SonicSprite.play("Walk");
movimiento.x = max(movimiento.x-aceleracion, -velocidadMax);
else:
movimiento.x = lerp(movimiento.x, 0, 0.09);
$SonicSprite.play("Idle");
if is_on_floor():
if Input.is_action_just_pressed("z"):
movimiento.y = fuerza_salto;
else:
$SonicSprite.play("Jump");
movimiento = move_and_slide(movimiento, arriba)
I had the smae problem, and it was solved for me adding the next after move_and_slide():
if velocity.y == 0:
velocity.y = 10
Apparently, if velocity is 0 after move_and_slide() it does not detect is_on_floor() anymore (bug?) so i added small velocity in direction of gravity.
About usinginput_(event), you do not need just_pressed because it only process when there is an input.. you can do somethin like this:
func _input(event):
if event.is_action_pressed("ui_up") and is_on_floor():
velocity.y = jump_speed
What i called velocity, i think in your script is called movimiento. Also, i think you are mixing gravities and velocities in movimiento. I'm sharing you my character scrpit so you can compare and see if it works better:
extends KinematicBody2D
var Bullet = preload("res://Bullet.tscn")
var speed = 200
var jump_speed = -300
var shot_speed = 100
var velocity = Vector2()
var grav = 980
var shooting = false
func _ready():
$AnimatedSprite.play()
$AnimatedSprite.connect("animation_finished",self,"on_animation_finished")
func _input(event):
if event.is_action_pressed("ui_up") and is_on_floor():
velocity.y = jump_speed
if event.is_action_pressed("shoot") and !shooting:
shooting = true
shot_speed = 20
velocity.y = -200
fire_weapon()
func _physics_process(delta):
velocity.x = 0
if Input.is_action_pressed("ui_right"):
velocity.x += speed
if Input.is_action_pressed("ui_left"):
velocity.x -= speed
if velocity.length() > 0:
if velocity.x < 0:
$AnimatedSprite.flip_v = true
$AnimatedSprite.rotation_degrees = 180
elif velocity.x > 0:
$AnimatedSprite.flip_v = false
$AnimatedSprite.rotation_degrees = 0
if shooting:
$AnimatedSprite.animation = "shot"
velocity.x = -shot_speed * cos($AnimatedSprite.rotation_degrees)
shot_speed *= 0.98
else:
if is_on_floor():
if velocity.x == 0:
$AnimatedSprite.animation = "idle"
else:
$AnimatedSprite.animation = "moving"
else:
$AnimatedSprite.animation = "jumping"
velocity.y += grav * delta
velocity = move_and_slide(velocity, Vector2(0,-1))
if velocity.y == 0:
velocity.y = 10
func on_animation_finished():
if $AnimatedSprite.animation == "shot":
shooting = false
func fire_weapon():
var bullet = Bullet.instance()
get_parent().add_child(bullet)
if $AnimatedSprite.flip_v :
bullet.position = $ShotLeft.global_position
else:
bullet.position = $ShotRight.global_position
bullet.rotation_degrees = $AnimatedSprite.rotation_degrees
bullet.linear_velocity = Vector2(1500 * cos(bullet.rotation),1500*sin(bullet.rotation))
Notice that i do not use gravity as a velocity or moviemiento; instead y multiply it by delta for getting velocity, as acceleration x time gives velocity.
I hope this helps.
This kind of problem can be solved using a Finite State Machine to manage your character controls and behaviour. You would play the Jump animation on entering the Jumping state from the Walking state. Utilizing correct design patterns early prevents spaghetti code in the future.
This is not a Godot specific solution, but definitely one worth your attention.
Let's dissect what's happening here, you fire up the code and ask your player to play the animation in one frame and then again in the other and in the next as long as the correct key is pressed or you in the correct state.
What the problem, the problem is that at every call of the function play it fires the animation again and again so it just restarts or another call runs separately and this causes unexpected behaviour.
The better way will be to manage the state of the player and animation player simultaneously and using that to perform animation calls.
enum State { IDLE, WALK, RUN, JUMP, INAIR, GROUNDED } ## This is to manage the player state
var my_state = State.IDLE ## Start the player with the idle state
As for the animation player state use the signal that you were talking about using the GUI or with code like below.
get_node("Animation Player").connect("animation_finished", this, "method_name")
## Here I assume that you have the Animation Player as the Child of the Node you the script on
And also hold a boolean variable to tell if the animation is playing or not.
if ( animation_not_playing and (case)):
animation_player.play("animation")
Turn it true or false as per your liking. Based on the animation finished signal.
In future, you might want to consider even using a simple FSM to maintain all this state data and variables.

Game Maker - Freezing an sprite on animation end

When the player dies, I want the sprite to stop animating on the last frame.
I tried this on Animation End event
if (sprite_index == spr_ninja_dead) {
image_speed = 0;
image_index = image_number - 1;
}
I also tried this
if (sprite_index == spr_ninja_dead) {
image_speed = 0;
image_index = 9;
}
This is the recommended way that GM suggests, yet the sprite freezes on the first sub image. What am I doing wrong?
When the player gets hit, the sprite would change to a flashing sprite image. Then an alarm would change it back to normal. I added an if statement to fix this:
if (sprite_index = spr_ninja_flash){
sprite_index = spr_ninja_idle;
}
What you want to do is run the animation first.
So try this:
if( sprite_index == spr_ninja_dead ){
if( image_index == image_number ){ // This will check if the image is the last image of the sprite
image_speed = 0;
}
}
First you check if the sprite is the dead one, then you check if it's the last image of the sprite and if that is the case you can pause the animation using the image speed.

GDScript: How to play an animation while key is preessed?

I am very new to coding and I'm still trying different languages out, I started off with GameMaker Studio and changed to Godot due to its compatibility with Mac I might as well learn something newer since GameMaker has been out for quite some time.
I want to create a RPG game and apply animation to each direction the character moves but the animation only plays after the key is pressed AND lifted. This means that while my key is pressed, the animation stops, and the animation only plays while my character is standing still, which is the complete opposite of what I want. The script looked really straight forward, but doesn't seem to be working.
I would tag this as the GDScript language instead of Python, but I guess I'm not reputable enough to make a new tag, so I tagged it under python because it is the most similar.
#variables
extends KinematicBody2D
const spd = 100
var direction = Vector2()
var anim_player = null
func _ready():
set_fixed_process(true)
anim_player = get_node("move/ani_move")
#movement and sprite change
func _fixed_process(delta):
if (Input.is_action_pressed("ui_left")) :
direction.x = -spd
anim_player.play("ani_player_left")
elif (Input.is_action_pressed("ui_right")):
direction.x = spd
anim_player.play("ani_player_right")
else:
direction.x = 0
if (Input.is_action_pressed("ui_up")) :
direction.y = -spd
anim_player.play("ani_player_up")
elif (Input.is_action_pressed("ui_down")):
direction.y = (spd)
anim_player.play("ani_player_down")
else:
direction.y = 0
if (Input.is_action_pressed("ui_right")) and (Input.is_action_pressed("ui_left")):
direction.x = 0
if (Input.is_action_pressed("ui_up")) and (Input.is_action_pressed("ui_down")) :
direction.y = 0
# move
var motion = direction * delta
move(motion)
As you check the input in _fixed_process, you call anim_player.play() several times a frame, which always seems to restart the animation, and thus, keeps the very first frame of the animation visible all the time.
As soon as you release the key, anim_player.play() stops resetting the animation back to start, and it can actually proceed to play the following frames.
A simple straight-forward solution would be to remember the last animation you played, and only call play() as soon as it changes.
You need to know if the animation has changed
First you need to put these variables in your code:
var currentAnim = ""
var newAnim = ""
And then you add this in your _fixed process:
if newAnim != anim:
anim = newAnim
anim_player.play(newAnim)
To change the animation you use:
newAnim = "new animation here"

Drag and Drop stage scrolling algorithm problems

I have some problems with basic drag and drop scrolling algorithm. Here is my algorithm:
When mouse pressed down i set boolean dragging = true and store the current mouse x and y position in stored_position variable.
When mouse up i set boolean dragging = false.
On each frame i check dragging == true and if it is i calculate the dx = current_mouse.x - stored_position.x and dy = current_mouse.x - stored_position.y. Then i store current mouse position as the new stored_position and scroll my view (it is 2d camera object) by this dx dy, as the Camera.x -= dx, Camera.y -= dy (i need the inversion one because of camera specific).
The problem with this algorithm is that when i drag the camera it starting to blink and move around/shake. I think it is because when i move my mouse from left to right it traces dx like this:
71
-67
69
-68
69
-68
8
-5
So i think it is the mouse twitching(i mean the mouse is jumps back sometimes when we try to draw a line). Any idea of changing algorithm, maybe i miss something?
Here is example of this problem: https://dl.dropbox.com/u/78904724/as_host/buld_build_other.rar (you need to run the index.html chose the level and try to drag the screen).
Updated
Here is the example full source link (this is the random picture, i swear): https://dl.dropbox.com/u/78904724/as_host/scroll_test.rar
And this is the code i used (in example i use native flash events instead of using axgl checks to not confuse someone, i have both examples and it cause the same problems):
//variables with comments
private var dragging:Boolean = false; //dragging flag
private var current_mouse:Array; //stored mouse position array [0] - x, [1] - y
private var d:Array; //dx dy array [0] - x, [1] - y
[Embed(source = "test.jpg")] public static const _sprite:Class; //sprite graphics
private var view_sprite:AxSprite; //some image on the stage to drag it
//this is the class constructor code
view_sprite = new AxSprite(0, 0, _sprite);
add(view_sprite);
current_mouse = new Array();
d = new Array();
Ax.stage2D.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
current_mouse[0] = Ax.mouse.x;
current_mouse[1] = Ax.mouse.y;
dragging = true;
});
Ax.stage2D.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
dragging = false;
});
Ax.stage2D.addEventListener(MouseEvent.MOUSE_MOVE, function(e:MouseEvent):void {
if (dragging) {
d[0] = Ax.mouse.x - current_mouse[0];
d[1] = Ax.mouse.y - current_mouse[1];
Ax.camera.x -= d[0];
Ax.camera.y -= d[1];
current_mouse[0] = Ax.mouse.x;
current_mouse[1] = Ax.mouse.y;
}
});
I am totally confused but the problem was this two strings(thanks the axgl author for helping me):
current_mouse[0] = Ax.mouse.x;
current_mouse[1] = Ax.mouse.y;
And when i remove them the dragging will work perfectly. But... I swear i tried this before and nothing gonna happed, camera just started to move faster without this and now... it works!
Thank you all for trying to help my. If someone would have the similar problems here is the full worked source: https://dl.dropbox.com/u/78904724/as_host/scroll_test_worked.rar
And this is the thread on axgl.org: http://axgl.org/forums/viewtopic.php?f=12&p=394

Which API can be used to _capture_ the mouse when moving OS X "Carbon" windows?

On request I have implemented support for moving an OS X window by dragging it using an area within the content part of the window, i.e replicating the drag and move functionality of the title bar but in another area.
The problem I have yet to resolve is the fact that if the user drags the mouse quickly it can leave the window area and then no more mouse move events are received.
On windows this type of problem can simply be fixed by calling the win32 method SetCapture(), what's the corresponding OSX method?
This application is a cross platform C++ application using Carbon for the OS X specific parts. (And yes, I know all about the Cocoa benefits but this is an older code base and there no time nor money for a Cocoa port at this point in time.)
I have found Carbon API methods like for example TrackMouseLocation() but can't really see how I could use them for this application. In listing 2-7 here http://developer.apple.com/legacy/mac/library/documentation/Carbon/Conceptual/Carbon_Event_Manager/Tasks/CarbonEventsTasks.html
the mouse is captured but the problem is that TrackMouseLocation() blocks waiting for input. Blocking is something this application can not do since it also host a flash player that must be called many times per second.
The protototype I have assembled when trying to figure this out basically looks like this:
switch(GetEventKind(inEvent))
{
case kEventMouseDown:
// A silly test to make parts of the window border "draggable"
dragging = local_loc.v < 25 || local_loc.h < 25;
last_screen_loc = screen_loc;
break;
case kEventMouseUp:
dragging = false;
break;
case kEventMouseMoved:
break;
case kEventMouseDragged:
if (dragging) {
Rect rect;
GetWindowBounds (windowRef, kWindowContentRgn, &rect);
int dx = screen_loc.h - last_screen_loc.h;
int dy = screen_loc.v - last_screen_loc.v;
rect.left += dx;
rect.right += dx;
rect.top += dy;
rect.bottom += dy;
SetWindowBounds (windowRef, kWindowContentRgn, &rect);
}
last_screen_loc = screen_loc;
break;
Any ideas appreciated?
I feel you should track mouse in Window as well as out of window. Following code should solve your problem,
EventHandlerRef m_ApplicationMouseDragEventHandlerRef;
EventHandlerRef m_MonitorMouseDragEventHandlerRef;
{
OSStatus ErrStatus;
static const EventTypeSpec kMouseDragEvents[] =
{
{ kEventClassMouse, kEventMouseDragged }
};
ErrStatus = InstallEventHandler(GetEventMonitorTarget(), NewEventHandlerUPP(MouseHasDragged), GetEventTypeCount(kMouseDragEvents), kMouseDragEvents, this, &m_MonitorMouseDragEventHandlerRef);
ErrStatus = InstallApplicationEventHandler(NewEventHandlerUPP(MouseHasDragged), GetEventTypeCount(kMouseDragEvents), kMouseDragEvents, this, &m_ApplicationMouseDragEventHandlerRef);
return true;
}
//implement these functions
OSStatus MouseHasDragged(EventHandlerCallRef inCaller, EventRef inEvent, void *pUserData){}
Hope it helps!!
I Hope It Help´s you too:
// Get Mouse Position --> WAY 1
printf("Get Mouse Position Way 1\n");
HICoordinateSpace space = 2;
HIGetMousePosition(space, NULL, &point);
printf("Mouse Position: %.2f %.2f \n", point.x, point.y);
// Get Mouse Position --> WAY 2
printf("Get Mouse Position Way 2\n");
CGEventRef ourEvent = CGEventCreate(NULL);
point = CGEventGetLocation(ourEvent);
printf("Mouse Position: %.2f, y = %.2f \n", (float)point.x, (float)point.y);
I´m looking for the way to get a WindowPart Reference at a certain location (over all windows of all aplications)
Certain methods in Carbon doesn´t work, always return 0 as a windowRef... Any ideas?
You could also try just calling DragWindow in response to a click in your window's content area. I don't think you need to implement the dragging yourself.

Resources