Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411281 Posts in 69324 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 09:50:31 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Game Maker For Beginners: Part II
Pages: [1] 2 3 ... 6
Print
Author Topic: Game Maker For Beginners: Part II  (Read 136261 times)
Derek
Bastich
Administrator
Level 10
******



View Profile WWW
« on: November 12, 2008, 10:47:15 AM »

Game Maker For Beginners: Part II

Main Topics: Inheritance, Object Interaction

Level: Total newb to Game Maker.  Suitable for programming nubs, also.

Abstract: We're going to make a simple, one-level side-scrolling shoot 'em up using Game Maker and GML.  In this second part, we're going to make some enemies and let you shoot at them!

Introduction

In the last tutorial we made some sprites, and we made some objects.  Now it's time to have them interact.  And by interact, I mean explode.

Create PlayerShot and EnemyBasic Objects

We're going to make a shot object (for the player to fire) and a basic enemy object (which we'll call "oEnemyBasic").  To do this, we'll make two sprites and then attach them to new objects.

For the enemy, though, we're going to do something a little bit different: we're going to make it an animated sprite, with four frames.  To do this, you can add some empty frames (Edit -> Add Empty), or simply copy-and-paste the first frame.  I'm going to make mine a little rotatey thingy.



By default, sprite animations will loop indefinitely.  You can preview the animation at various speeds in the Sprite Editor.  However, you have to set the in-game speed of the sprite in the actual object (see below).

Inheritance

One of the nice features of Game Maker is inheritance.  This is actually a very important concept in object-oriented programming, so you should understand it.  The idea is that objects can inherit traits from other objects.



In our shoot 'em up, we know we'll be blasting all kinds of enemies... some will fly right at us, some will follow a pattern, and some will simply sit there and shoot at us.  But they all have at least two things in common: 1. we can blow the crap out of them, and 2. they can blow the crap out of us.

Without inheritance, we'd have to redefine the same shared variables over and over again, and if we wanted to make a change to all the enemies, we'd have to do it once for every enemy object we had.  And if, for example, we wanted to test the player's collision against an enemy, we'd have to do that multiple times, too.

Instead, let's make an oEnemy object from which all our enemies will inherit.  This object is called the parent, and the objects that inherit from it are called its children.  oEnemy will house all the variables are shared by every enemy.  If we want to do some kind of test against all enemies, we can write one test, against oEnemy, as opposed to one for each enemy type.

Quote from: PROTIP
You will probably never use an instance of oEnemy in the game, only its children.  It's just a "base" object that defines a type of object.

Let's start by giving oEnemy a single variable "hp = 1."  So by default, all enemies will take one damage before exploding.  That's all we need for now.



Now we'll open up our oEnemyBasic object.  On the left-hand side, you can see a "Parent" drop-down menu.  Set this to "oEnemy."  oEnemyBasic is now the child of oEnemy.  By default, it will also have the variable "hp = 1."

Quote from: PROTIP
You can define a variable in a child object that is already defined in its parent.  Whichever definition comes last will take precedent (see below).

If you drop an enemy into the room and run the game, you'll notice the animation is going really fast.  To change the animation speed, you have to set the variable image_speed (preferably in the Create Event).  The default is 1, which is 1 frame per step.  For something more reasonable, try 0.4.

Quote from: PROTIP
TAKE NOTE!  If you add an event to a child object, it will overwrite the same event of the parent!  To also run the parent's event, use the "control -> Call Inherited Event" action (), which should proceed the child's code.

Let the Player Shoot

Alright, we have our enemy (which currently just sits there), so now our guy needs to be able to shoot.  First, let's make the shot move.  In oPlayerShot:

Code: (Create Event)
xVel = 8;

Code: (Step Event)
x += xVel;

That will add "xVel" (x velocity) to the shot's x position every step.  And since we defined xVel as 8, it will move 8 pixels every step.

Now let's make it so that when you press the "X" key, your ship fires a bullet.  Add this code to the Step Event of your oPlayerShip:

Code:
if (keyboard_check(ord('X')))
{
    instance_create(x, y, oPlayerShot1);
}

Some new bits of syntax here

ord - to check for any of the letter (A-Z) or digit (0-9) keys, use the "ord" function.  This evaluates to a number that represents the key.  For special keys, like the arrow keys, there are "constant" (unchangeable) variables defined for them, like vk_up, vk_down, etc.

instance_create(xpos, ypos, object) - this is a very useful function!  As the name implies, it creates an instance of the given object at the given position.

Quote from: PROTIP
Be aware of the "origin" of your sprites, which you can change in the sprite editor.  This is the point of reference for the objects, and corresponds to (x,y).  By default, the origin is the upper-leftmost corner of the sprite, but it makes sense conceptually to set this to center for certain objects, like projectiles.

Let's test out our code... holy crap!



It's like a machine gun.  That's because Game Maker is checking if "X" is pressed EVERY STEP, and creating a shot if it is.  There are a couple of ways to fix this:

1. Add a firing delay

In a lot of old shoot 'em ups, you can hold down the button to shoot at a steady rate, or you can tap the button to shoot faster.  We can emulate that with this code:

Code: (Create Event)
firingDelay = 0;
firingDelayMax = 10;

Code: (Step Event)
if (keyboard_check(ord('X')))
{
    if (firingDelay > 0)
    {
        firingDelay -= 1;
    }
    else
    {
        instance_create(x, y, oPlayerShot1);
        firingDelay = firingDelayMax;
    }
}
else
{
    firingDelay = 0;
}

Try to make sense of it - we've added a firingDelay variable that ticks down while the key is pressed.  And when you let go of the key, it sets the delay to 0 so that as soon as you tap the key again, you fire off a shot.

2. Only fire a shot when the key is first pressed

The easiest way to do this is to use Game Maker's Key Press event (), which activates when the key is first pressed down.

Let's stick with the first solution, though!  Test it out and make sure it looks right!



Make the Enemy Blow Up

Alright, it's time for the money shot.  KABLAM!  First, let's make an explosion object.  You know the drill - create a sprite, then create an object.  As with any animated object, you'll probably want to set the image_speed to something less than 1.



One new event we'll want to use is the "Animation End" event, which is found in the "Other" group in the Event Selector.  This event is called when the current animation ends.  We want to destroy the explosion object after it's finished playing its animation (otherwise it will sit on the screen and loop).

To do this, we invoke the instance_destroy() function.  It takes no arguments.

Now that we've got a shot, that shoots, and an explosion, it's time for kablooie!  To do make this happen, we're going to use a Collision event.  Specifically, we're going to check when oShot collides with oEnemy (remember, we want to check against the base enemy object!).

Once this collision happens, we'll do two things: 1. we'll subtract 1 from the enemy's hp, and 2. we'll destroy the shot.  The code looks like this:

Code:
other.hp -= 1;
instance_destroy();

other is a special variable which represents the other instance in the event.  In this case, it represents oEnemy.  There's also self (oPlayerShot in this event), and global (used for global variables who's scope crosses all objects and rooms).  We'll talk about global more later, since that's a useful one.

By using the "." notation we can set the variable of another instance.  It's quite handy, as you can see.

Finally, oEnemyFlyer should do a check (in the Step event) for when its hp is less than 1.  When this happens, it should create an explosion and then destroy itself:

Code:
if (hp < 1)
{
    instance_create(other.x, other.y, oExplosion);
    instance_destroy();
}

Create and then destroy - it's the circle of life!  Lookin' good!  Now can you make the enemies do damage to you when they touch you?



[Download the Source for this Tutorial]

Next Up: We'll build the level, and make the screen scroll! [link]

« Last Edit: October 01, 2009, 02:13:22 AM by Derek » Logged
Derek
Bastich
Administrator
Level 10
******



View Profile WWW
« Reply #1 on: November 12, 2008, 11:10:41 AM »

Let me know how the pacing is for you guys.  I'm trying to cater to complete beginners without getting too bogged down.
Logged
Terry
TIGSource Editor
Level 10
******



View Profile WWW
« Reply #2 on: November 12, 2008, 11:16:55 AM »

Excellent tutorial! I started messing about with Game Maker for the first time last weekend, but I didn't know about that inheritance thing! That should make things a lot easier, cheers!
Logged

ஒழுக்கின்மை (Paul Eres)
Level 10
*****


Also known as रिंकू.


View Profile WWW
« Reply #3 on: November 12, 2008, 11:49:32 AM »

One thing I dislike about how inheritance works in GM is that events replace events of parents. From what I understand and my limited testing in the matter, if you have a step event for a parent, and a step event for a child, only the child's step event will be performed, not both. Only when the child doesn't have a step event will the parent's step event be performed.

I myself do not use inheritance much. What I do instead is have very few objects. I.e. instead of having a separate object for every enemy type, I have *one* object encompassing all enemy types, and distinguish them by a variable called type (their type being determined when I use instance_create). Depending on their type, they look and act differently, but because it's just one object it's easier for me to maintain. But this is a matter of taste, I just do it because of startup speed issues: the fewer objects and other stuff you have embedded in GM, the faster the game starts up. Optimally it's a good idea to have absolutely no resources at all in the GM file, and just have it load all of them externally, and keep objects down to only a few (less than 10), that gives you the quickest startup time.
Logged

Derek
Bastich
Administrator
Level 10
******



View Profile WWW
« Reply #4 on: November 12, 2008, 12:07:40 PM »

One thing I dislike about how inheritance works in GM is that events replace events of parents. From what I understand and my limited testing in the matter, if you have a step event for a parent, and a step event for a child, only the child's step event will be performed, not both. Only when the child doesn't have a step event will the parent's step event be performed.

That's actually not true!  (Well, it's true, but...) There's an action called "Call the Inherited Event" which calls the parents' event.

And I need to mention that in the tutorial!  EDIT: Oh, wait, I already did.  I'll add the icon, then.
« Last Edit: November 12, 2008, 12:13:05 PM by Derek » Logged
ஒழுக்கின்மை (Paul Eres)
Level 10
*****


Also known as रिंकू.


View Profile WWW
« Reply #5 on: November 12, 2008, 12:09:03 PM »

Hmm, I never heard of that -- will look into it.
Logged

Derek
Bastich
Administrator
Level 10
******



View Profile WWW
« Reply #6 on: November 12, 2008, 12:11:36 PM »

It's under "control" and it looks like this:
Logged
ஒழுக்கின்மை (Paul Eres)
Level 10
*****


Also known as रिंकू.


View Profile WWW
« Reply #7 on: November 12, 2008, 12:12:37 PM »

Ah, I never use drag and drop, so that's why I missed it. Maybe now I'll start using inheritance more.
Logged

Skofo
Level 10
*****



View Profile
« Reply #8 on: November 12, 2008, 12:21:07 PM »

Ooh, purdy.

I've always enjoyed the smooth pacing and personality of these tutorials. I've linked a never-programmed-in-her-life friend of mine to the first one of this series a couple weeks ago and have been trying to learn how to make gesture drawings from your other tutorial.

Keep them coming!
Logged

If you wish to make a video game from scratch, you must first invent the universe.
Inane
TIGSource Editor
Level 10
******


Arsenic for the Art Forum


View Profile WWW
« Reply #9 on: November 12, 2008, 12:53:14 PM »

Isn't the image speed per step and not per second? Tongue
Logged

real art looks like the mona lisa or a halo poster and is about being old or having your wife die and sometimes the level goes in reverse
ஒழுக்கின்மை (Paul Eres)
Level 10
*****


Also known as रिंकू.


View Profile WWW
« Reply #10 on: November 12, 2008, 12:55:15 PM »

Yes:

Quote
image_speed The speed with which we cycle through the subimages. A value of 1 indicates that each step we get the next image. Smaller values will switch subimages slower, drawing each subimage multiple times. Larger values will skip subimages to make the motion faster. Sometimes you want a particular subimage to be visible and don't want the program to cycle through all of them. This can be achieved by setting the speed to 0 and choosing the correct subimage.
Logged

Derek
Bastich
Administrator
Level 10
******



View Profile WWW
« Reply #11 on: November 12, 2008, 12:55:28 PM »

Oh jeah, so you are both correct.

Skofo: Beer!
Logged
cactus
Makeout King
Level 5
******



View Profile WWW
« Reply #12 on: November 12, 2008, 03:53:39 PM »

Ah, I never use drag and drop, so that's why I missed it. Maybe now I'll start using inheritance more.

There's also event_inherited()  Wink
Logged
ஒழுக்கின்மை (Paul Eres)
Level 10
*****


Also known as रिंकू.


View Profile WWW
« Reply #13 on: November 12, 2008, 03:54:35 PM »

Excellent, thanks! Don't know how I missed that one too ;_;
Logged

___
Vice President of Marketing, Romeo Pie Software
Level 10
*


View Profile
« Reply #14 on: November 12, 2008, 04:33:20 PM »

The call parent events and event_inherited() saved my LIFE.
Logged
BenH
Level 2
**


Dirty boy, wash your hands!


View Profile WWW
« Reply #15 on: November 12, 2008, 04:55:55 PM »

One thing I dislike about how inheritance works in GM is that events replace events of parents. From what I understand and my limited testing in the matter, if you have a step event for a parent, and a step event for a child, only the child's step event will be performed, not both. Only when the child doesn't have a step event will the parent's step event be performed.

I didn't use the inheritance for the same reason. I figured it made the whole thing pretty pointless, but now I know about "call parent events" and event_inherited() I'll probably start using it! Thanks for pointing that out Grin
Logged

KniteBlargh
Level 4
****


Blargh...


View Profile WWW
« Reply #16 on: November 12, 2008, 08:47:52 PM »

Let me know how the pacing is for you guys.  I'm trying to cater to complete beginners without getting too bogged down.
I'm pretty much as beginner as anyone could possibly be, plus I'm so stupid that not even the Idiot or For Dummies books work for me, and yet I'm finding all of your tutorials so far useful and easy to follow. I depend upon your tutorials Derek. Evil

I'll let you know how my little follow-along project is doing once I apply the things I've learned from this tutorial to it.
Logged

GregWS
Level 10
*****


a module, repeatable in any direction and rotation


View Profile
« Reply #17 on: November 14, 2008, 11:08:57 PM »

Well, I'd never heard about this parent/child step problem before either, but I have used inheritance as a system before (and will likely use it again), so this is really really good to know!  Grin
Logged
GregWS
Level 10
*****


a module, repeatable in any direction and rotation


View Profile
« Reply #18 on: November 15, 2008, 01:36:53 AM »

Oh, and because this topic involved inheritance (in the programming sense), I thought I'd once again say how incredible I think it is that inheritance (in the way we understand it from a coding point of view) is remarkably similar to Aristotle's critique of Plato's theory of Forms.
Logged
Carnivac
Level 0
***



View Profile
« Reply #19 on: November 16, 2008, 04:06:17 AM »

Nice.  I didn't know about those commands but I have been using parent objects a lot.  For me GML suddenly became a lot more managable and less tedious once I learnt about the whole parent thing.
Logged

Pages: [1] 2 3 ... 6
Print
Jump to:  

Theme orange-lt created by panic