• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM Side scroller projectile help

A

AtomicToilet

Guest
Hello!
I'll preface this with: I've eyeballed a whole bunch of related tutorials, but for some reason can't seem to find (or correctly tweak) an existing one to fit with what I'm doing:

I'd like to trigger a projectile within an attack script.

I can't seem to figure out how to tell the projectile to
a) fire in the right direction
b) animate
c) only create one projectle when the player changes to the attack state
------------------
I have an attack script set up and the animation and state change work perfectly, but...

I use instance_create(obj_player.x,obj_player.y,obj_projectile) at the start of the attack script, and of course this works, except it fires out a string of projectiles and they don't animate ( have object speed and image speed set in the obj_projectile's create event, but only the object speed works).

To perhaps clarify, a lot of the tutorials seem to have the relevant projectile code in the player step and/or create event - I understand how these work, but because I want to create a projectile in the attack script, I can't get my head around what code needs to go where. If anyone can point me in the right direction, that'd be ace!
 

kburkhart84

Firehammer Games
It seems like your attack script is running over and over while in the attack state, so if that is the case then yes indeed it will repeatedly create that projectile. The solution quite often involves a separate variable, like "canShoot" or something. When you first get to the attack state, this variable is true, and as soon as that one shot gets off, you set it to false. Then the shooting would be inside an if{canShoot} statement.
if(canShoot)
{
canShoot = false;
instance_create(.....);
}

About the lack of animation in the bullet itself, I'd have to see the code in that create event. Typically you don't even mess with changing image_speed because the actual sprite animation frames are at the right speed. But you can change it to make it faster or slower of course.
 
A

AtomicToilet

Guest
Cheers for the reply, kburkhart84!

I wrapped the attack script in a statement as you suggested, and - in the player object - added the shuriken_attack = false variable in thje create event AND an Animation End event so that the player reverts back to their original move script/state afterwards*, and this technically all works...except now the player stays on the spot when the attack is triggered (even in mid-air) and doesn't create a projectile at all.

Here's the attack script - it's lengthy, because I incorporated the move script so that the player is able to move and attack, and the correct animation plays at the right point. I've annotated the first part with regards to your advice/the outcome. And I've commented out an end 'else' statement because it seemed to muck things up, too.

Code:
///scr_player_shuriken


if (shuriken_attack == false)       //If I remove this...
{                                   //and this...
shuriken_attack = true;             //and this... and the closed } at the end...
image_speed = .2;                   //then this code works fine, except the shuriken
sprite_index = spr_player_shuriken; //spawns in a short stream...
instance_create(x,y,obj_shuriken);  //rather than as a single object

//THE MOVE SCRIPT CODE NEEDS TO BE IN HERE
//OTHERWISE THE PLAYER STAYS WHERE HE IS AND ATTACKS

//React to inputs
move = key_left + key_right;
hspd = move * spd;
if (vspd < 5) vspd += grav;


//Heartbeast platformer jump code
if (!place_meeting(x,y+1,obj_wall))
{
    image_speed = .2
    sprite_index = spr_player_shuriken;
    vspd += grav;
  
   //Control jump height
    if (key_up_released && vspd < -5)
    {
    vspd = -5;
    }
}
else if (place_meeting(x,y+1,obj_wall) and hspd !=0)
{
    image_speed = .2
    sprite_index = spr_player_shuriken_run;
    vspd = 0;
    if (key_up)
    {
    vspd = -10;
    }
}


//Acceleration code
if (key_right || key_left)
{
    hspd += (key_right - key_left)*acceleration;
    hspd_dir = key_right - key_left;
    
    if (hspd > spd) hspd = spd;
    if (hspd < -spd) hspd = -spd;
}
else
{
    //Heartbeast platformer friction code
    //scr_player_friction(acceleration);
    
    //Original Friction code
if(!key_right && key_left = 0 && hspd != 0)
{
    hspd = ((abs(hspd)- fric) * sign(hspd));
}
else
{
    hspd = move * spd;
}
}


//Horizontal Collision
if (place_meeting(x+hspd,y,obj_wall))
{
    while(!place_meeting(x+sign(hspd),y,obj_wall))
    {
        x += sign(hspd);
    }
    hspd = 0;
}
x += hspd;

//Vertical Collision
if (place_meeting(x,y+vspd,obj_wall))
{
    while(!place_meeting(x,y+sign(vspd),obj_wall))
    {
        y += sign(vspd);
    }
    vspd = 0;
}
y += vspd;

}

//else
//{
//shuriken_attack = false;
//state = scr_move_state_platformer;
//}
To streamline things, I only put speed = 5 in the shuriken object create event (although, if I understand correctly, this will automatically and every time, send it to the right...?).

*Here's the code for this, just in case it's handy:

Code:
///Change the player state
if (state ==  scr_player_shuriken)
{
    state = scr_move_state_platformer;
    shuriken_attack = false;
}
 

kburkhart84

Firehammer Games
If you have it working with the moving code in there, that's cool. Might I suggest you put all that movement code in a script though, and just call that script. This let's you have the code in one place and you can call it from whichever state you like that allows the movement.

One issue I see though, you could have shooting not be its own state, since it isn't something that is permanent if you get what I mean. I assume the shooting is only a few frames of animation right? I would personally just keep whatever state you are in and execute the one-time shot. Then on the animation_end, change the sprite back depending on the state you are in. This way, your movement can stay separate from the shooting.

Another thought if you really need it is to have 2 separate state machines, one for movement, and the other for shooting. Then you would have to look at both to determine which sprite animation to play, but it lets you shoot and move all at the same time.
 
A

AtomicToilet

Guest
You're not going to believe this, but once I re-did this code at the end of the attack script, it now works almost perfectly!
Code:
else
{
shuriken_attack = false;
state = scr_move_state_platformer;
}
No idea why. The wonder of refamiliarising myself with Gamemaker and not knowing quite what I'm doing hahahaha

The only thing I need to do now is make the projectile actually go in the direction the player is facing. I know I can use image_xscale to achieve this, but I can't think how to implment it in relation to this projectile object - would it go in the attack script, under instance_create, or in the projectile object create event?
 

kburkhart84

Firehammer Games
It would likely want to be in the attack script, because the creation event doesn't have any way to know what direction it is supposed to go. The instance_create() function returns a value that is the specific id for that instance of the bullet. So, you can do a with() statement with that value, and then in that with statement set the direction, etc...
Code:
shuriken = instance_create(.....);
shuriken.direction = .....
Code:
with(instance_create(....))
{
    direction = .....;
}
 
A

AtomicToilet

Guest
Ah ha! Thank you! Almost sorted! In the obj_shuriken Create Event, I put s = -1; and then in the attack script I put

Code:
if (shuriken_attack == false)       
{
image_speed = .2;                   
sprite_index = spr_player_shuriken;                                   
shuriken_attack = true;             
s = instance_create(x,y,obj_shuriken);
with (s)
{
    if (other.image_xscale = 1)
    {
    s.direction = 0;
    speed = 5;
    }
    else
    {
    s.direction = 180;
    speed = 5;
    }
}
before all the first //movement comment and it now fires a projectile with every attack key press, in the direction the player is facing. Hurray!

BUT...I've actually realised there is another slight issue with my attack code, if I use the aforementioned else section right at the end:

the attack animations no longer play properly (ie. they're either superfast or don't complete their animation cycle, I can't quite tell)

I'm guessing because it's reverting back to the standard move state too quick...? Not sure how to fix this, though (removing the else code once again causes the player to stop on the spot when they attack, even if they're mid-air). Could I maybe somehow use an alarm so that the game waits a moment before switching states, therefore (I assume) giving the attack animations enough time/frames to actually play...?
 

kburkhart84

Firehammer Games
Yeah, as soon as the code in that else{} executes you are putting the state back to the platformer state, which I'm guessing is changing the sprite. You could either code the variable to count up from zero, and on 1 do the shurican creation, and then on 10 switch back states, or you could use an alarm(would do the same thing basically), or you could have an animation end event and have it verify it is in the attack state and if so switch back.
 
A

AtomicToilet

Guest
The issue I'm having now is I'm not sure where to put the alarm so that it actually has an impact. I understand the alarm should be involved with state change code, because I need the script to 'wait' before it switches scripts, and I've put an alarm event in obj_player, with a ///comment to make sure it is used, but whereever I put the actual alarm, it seems to get ignored. I've tried placing it in the attack script:
  • before all the actual code.
  • inside the if statement code (at the top).
  • as part of the end else statement.
  • inside the end else statement.
  • In the attack script, inside the end else statement in place of state = scr_move_platformer (this allows the player to move and fire, but they fire off a stream of projectiles - I guess because, although it does then revert to the move script [since I moved this state change to an Animation End event, as noted below], it's still running the attack script a bunch of times in quick succession...?
I also have an Animation End Event in obj_player, that originally had this code:

Code:
if (state == scr_player_shuriken)
    {
    state = scr_move_state_platformer;
    shuriken_attack = false;
    }
But this can be safely removed since the same thing happens inside the end of the attack script. However, I still tried adding an alarm to this code first. I put it:
  • as part of the if statement as an && alarm==
  • then inside the brackets
And finally, I tried this with both the Animation End event code above, and the attack state end else code (but not at the same time):
  • An alarm both as part of the if condition and inside the following brackets, because I started to go a little crazy
In all these examples, I messed around with different alarm values, from 0 to 240, and the only time it had any effect was the bit I highlighted in green.
 
Top