• 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!

GameMaker A simple question

E

EvilShuckle

Guest
So I have this simple question. Hopefully you can explain it in a way that my inexperienced *ss also understands it but here goes.

To understand the basics I've been following these youtube vids by Shaun Spalding. In one of the earlier vids he showed how to make an enemy react when hit by a bullet. A variable health, when hit a flash of white light, and when at 0 health, a small knockback and another sprite to make it look dead. This is all clear to me and I can make (but haven't done because there are no bullets in my game).

However in my game it's about melee attacks. In another guide he explained how to do a melee attack, with hitbox and all, that also hits the opponent. The idea was to use different states.

The one in the player object is:
Code:
hsp = 0;
vsp = 0;
grv = 0.3;
walksp = 4;

hascontrol = true;

state = PLAYERSTATE.FREE;
hitByAttack = ds_list_create();

enum PLAYERSTATE
{
    FREE,
    ATTACK_SLASH,
    ATTACK_COMBO
    
    
    
    
}
I don't fully understand it. What does the ds_list_create() do exactly?

In the state attack slash it is used again


Code:
hsp = 0;
vsp = 0;

if (sprite_index != atkground)
{
    sprite_index = atkground;
    image_index = 0;
    ds_list_clear(hitByAttack);
}

mask_index = atkgroundHB;
var hitByAttackNow = ds_list_create();
var hits = instance_place_list(x,y,skeletonow,hitByAttackNow,false);
if (hits > 0)
{
    for (var i = 0; i < hits; i++)
    {
        var hitID = hitByAttackNow[| i];
        if (ds_list_find_index(hitByAttack,hitID) == -1)
        {
            ds_list_add(hitByAttack,hitID);
            with (hitID)
            {
                game_restart();
            }
        }
        
    }
}
ds_list_destroy(hitByAttackNow);
mask_index = playerstanding;

if (animation_end())
{
    sprite_index = playerstanding
    state = PLAYERSTATE.FREE;
}
It works, after some bug fixing, but exactly what it does I don't know. The same ds_list_create is used. Basically I understand it til 'for (var i = 0; etc'

Now my actual question: In this game he put together the enemy also makes use of a script to go through the whole process of checking whether hit, showing an animation when hit, losing health and eventually crumbling when dead. Problem is, he doesn't explain the script or show it, really. He shows it for like 1 second but isn't complete, and so I don't know how that one worked.

Currently in my game I am using game_restart(); in order to see if it works. Which it does, now. So that's good, only I don't know which code I should add there to make it work. I know how to do the basic stuff after colliding with an object (the bullet), but since the melee attack is just a change in sprite and not in object, I don't know how to make it so that the stuff that'd happen with the bullet (the white flash, losing health, getting knocked back when dead, changing sprite) now happens when the creature gets hit by the melee attack.

The flash of white is a shader that is white. The enemy currently has in the create:

Code:
walksp = 4;
hp = 4;
flash = 0;
and in draw
Code:
draw_self();




if (flash > 0)
{
    flash--;
    shader_set(sWhite);
    draw_self();
    shader_reset();
}
Hopefully this is enough information
 
C

Catastrophe

Guest
Honestly sounds like you should find a different tutorial, this seems like it is ridiculously overcomplicated. ds_lists and enums are a bit overkill in a newbie tutorial.

hitByAttack is used to make sure you don't hit enemies twice in the same attack. If you read the code closely:

if (ds_list_find_index(hitByAttack,hitID) == -1)

it is added onto and checked if the hit instance is not already in the list hitByAttackNow list (the one created for this frame) and is reset at the beginning when you do another attack.

But it's better to give enemies an invincibility timer. I'd really advise against this tutorial if you're a newbie tbh.

with (hitID)
{
game_restart();
}

^ this will affect the enemy, not the player. With takes an id (in this case, hitID is the enemy you hit) and does code as if it were being run in that instance's scope. So if your enemy has an "oh ni I'm hit" function, you can just put it right over game_restart()

For instance, simply putting "flash = 60" there will make the enem flash.
 
Last edited by a moderator:
E

EvilShuckle

Guest
Ah I see, thanks Catastrophe. And yeah as far as newbie tutorials go, it was a bit complex. But is there a way to do make a melee attack easier? The concept of different states and not actually going to a different object seems like a logical way to make it and seems like a good way to get started with. I remember from years ago when I avoided coding at all cost because I plainly suck at it, I actually made a new object which does work but it is such a chaotic and messy way of doing things.

But I'll go try adding the code there and see if it works. It was mostly due to me not understanding what I did there that I didn't try that yet
 

Yal

🐧 *penguin noises*
GMC Elder
The idea with a melee attack is that you go through an attack animation, overriding all normal movement and animation code. The "state" approach idea is that the player should change between a "normal movement" state and a "melee attack" state so you can control what code is run in what state better (in melee state, you go through the attack animation and then return to normal state automatically, and you also hurt enemies on touch. In normal state, you have your normal movement). Knowing what the point of the state machine is probably helps, I can feel like Shaun misses that in his tutorials sometimes.

State machines also get a lot easier to visualize if you draw diagrams of how to transition between states. Draw these on paper first, and you'll have a much easier time coding state machines.
upload_2019-10-27_15-25-13.png
 
L

lemonhead

Guest
I can't say enough about how state machines have simplified my life. Some of the most useful video tutorials I have seen on the subject were by Friendly Cosmonaut:


I think she explains it very clearly and uses what I consider to be a lot of best practices. Also she is good at avoiding code coupling which is a very important design principle to pick up early on. It will prevent many a headache later.
 
This is off the top of my head, but for me the simplest way of approaching this is to have the player object controlling the enemy, and tell them when they should / shouldn't react to damage.

In the enemy object:
create event:
Code:
check_damage = false;
was_attacked = false;
in player object step: (this can be simplified even further, but for now I'll go with the most basic way)
Code:
if doing melee attack // pseudo code here
{
enemy object.check_damage = true;
}
else
{
enemy object.check_damage = false;
enemy object.was_attacked = false;
}
in the enemy object:
step:
Code:
if !was_attacked
{
if check_damage
{
if found collision with player // pseudo code
{
do response // pseudo code
was_attacked = true;
}
}
}
If I'm figuring that through correctly, that should be enough to ensure an enemy only registers one hit per attack, and then the player object resets the enemy state when it's own attack sequence finishes.

NOTE:
One side effect of doing it this way, is that it sets all enemy objects to check for collision, for as long as the player attack state lasts.

This is unnecessary, and more expensive than it needs to be, but narrowing down which enemy objects are affected obviously makes things more complex. So, for the sake of simplicity, I haven't done that here.
 
Last edited:

FrostyCat

Redemption Seeker
I don't fully understand it. What does the ds_list_create() do exactly?
This starts a new list in which to place IDs of instances that it has already bumped into. You could have learned what that does in the Manual's entry for that function.

It works, after some bug fixing, but exactly what it does I don't know. The same ds_list_create is used. Basically I understand it til 'for (var i = 0; etc'
The loop simply goes through every position in hitByAttackNow (0 to hits-1), and inserts each value that is not already in hitByAttack into hitByAttack. Again, this is basic material that you should know well before touching that tutorial, especially the for loop which is a basic syntax element.

I recommend that you postpone these tutorials AND your current dream project until you learn to use the Manual's index tab and understand every element under this section. Attempting to follow tutorials without knowing syntax is like trying to play Chess without knowing how every piece moves. If you don't see that the for loop means "for every position in the hitByAttackNow", you need to brush up on basic syntax or you'll be walking blind.

Spalding didn't explain it in detail because that's the level of experience he expects of people following his tutorial --- knows the basic pieces and rules, but not patterns for putting them together. You need to be at the level he expects before you can truly benefit.
Honestly sounds like you should find a different tutorial, this seems like it is ridiculously overcomplicated. ds_lists and enums are a bit overkill in a newbie tutorial.
Sorry, but I'm absolutely annoyed at this attitude about data structures being overkill for novices. It's an essential skill for all levels, just not one that mainstream GML education values enough.
 

Yal

🐧 *penguin noises*
GMC Elder
I'm firmly in the "data structures is too advanced for newbies" camp, I did some really stupid stuff with them back in the GM6 days (like trying to use a ds_map to sort the available actions in an RPG... which is clearly the best structure for that job) and I ended up giving up on them for years after all the bugs I caused being in over my head. (I still use arrays whenever possible because of not having to deal with garbage collection, but that's just a personal preference)

Once we get lightweight data objects, it'll be a much smoother transition from basic GM stuff to data structures... then you can build on top of what people already know about how objects work.
"Hey, you know how objects represents stuff in the game world? They can represent abstract things like data as well! Let's say you want to have a turn order in an RPG, this is a perfect match for a queue..."
*explains how to make your own queue data structure*
*note at the end about how GM has this built-in with ds_queue so you don't need to reinvent the wheel*
"oh yeah right now the turn order is first in, first out... but usually the speed stat influences who goes first. Is there any way we could achieve that?"
*explains priority queues*
 
I'm firmly in the "data structures is too advanced for newbies" camp
Whilst the topic might well be something for more advanced users, the ds_list_find_index function is quite a straightforward and simple way of limiting interactions so they happen only one time. That is what is being done in the tutorial, and it's a relatively easy solution for a moderately tricky thing (if looking at it as a newbie...?)

Which leads to the conundrum of: introducing the (maybe a bit green) programmer to relatively complex things like data structures, though how it is then used is a fairly basic solution with an out of the box function. It's simple, which is a good thing...isn't it?

Compare that to organising responses between various objects / instances, and understanding how "with" works, or what is "other", or how to get an individual id, and so on, and so on. The way with the "easier" solution (not involving data structures) ends up more bloated, and requires the programmer to oversee a more intricate design. That, in itself, is a more advanced mindset / skill that I would think inexperienced programmers don't have.
 
Last edited:

Yal

🐧 *penguin noises*
GMC Elder
When you don't know how anything works, it's easy to make mistakes... and the more powerful tools you play around with, the more catastrophic consequences. Remember the days when there was a line in the manual like "the assignment of IDs change each step so you can't use values from previous steps" (was instance_id[] or some function related to it, I believe) and hundreds of people wrote buggy workarounds to refer to objects in code thinking id changed each step? The Heartbeast / RPG movement using the physics system is another thing like that, and there was a point where it lead to several topics by people having issues with the physics system per day not too long ago. Between memory leaks and the quirks with data structure IDs being reused (aka, not guaranteed to be unique) I feel it's a good attitude to keep them locked up until people are past the copypasting-stuff-they-don't-understand-yet stage, for their own sanity's sake.
 
Top