Player movement and sprites

BlitzMax Forums/BlitzMax Beginners Area/Player movement and sprites

XxGuardianKnightxX(Posted 2009) [#1]
I am trying to code a simple player/sprite movement engine, which will hopefully evolve into my first game as I add more to it. Basically I want to have it so my player moves in 8 directions, viewed from a top-down view.

I am baffled as what to do next. I created some example code to convey what I DO know so far...but it is totally not complete and I gave up halfway for now because I didn't know what else to put.

'*******************************
'*      PLAYER MOVEMENT        *
'*        AND SPRITES          *
'* --------------------------- *
'*  this code does NOT work    *
'*    and is meant only        *
'*    as a demonstration.      *
'*                             *
'*      DO NOT COMPILE         *
'*******************************

' Set up graphics mode
Graphics 640,480,0
SuperStrict' define every variable myself

'******** SPRITES (IMAGES) ********

' Mask all images
SetMaskColor 255,0,255

' Load the player images (4 cardinal directions)
spr_n = LoadAnimImage("n.png",64,64,0,4)' facing up (north)
spr_s = LoadAnimImage("s.png",64,64,0,4)' facing down (south)
spr_e = LoadAnimImage("e.png",64,64,0,4)' facing right (east)
spr_w = LoadAnimImage("w.png",64,64,0,4)' facing left (west)

' Load the player images (4 intermediate directions)
spr_ne = LoadAnimImage("ne.png",64,64,0,4)' diagonal up-right (northeast)
spr_nw = LoadAnimImage("nw.png",64,64,0,4)' diagonal up-left (northwest)
spr_sw = LoadAnimImage("sw.png",64,64,0,4)' diagonal down-left (southwest)
spr_se = LoadAnimImage("se.png",64,64,0,4)' diagonal down-right (southeast)

'******** TYPES ********

' Create the player type
Type t_player
	Field x_pos: Int' x_pos is an integer
	Field y_pos: Int' y_pos is an integer
	Field c_sprite: t_csprite' c_sprite is a type
End Type

' Create the csprite type
Type t_csprite
	Field c_frame: '?
End Type	

' Create new player instance
player:t_player = New t_player


I just want to know how I can have it so the player type holds the values for both the current "state" (walking/standing in different directions...ultimately I'll even add attacking animations, but that comes later) and the current frame of the current animation. Or is there a simpler way to do it? It all just makes my head spin.


tonyg(Posted 2009) [#2]
Have two fields : State and current_frame?


XxGuardianKnightxX(Posted 2009) [#3]
Elaborate? I don't follow.


tonyg(Posted 2009) [#4]
...


tonyg(Posted 2009) [#5]
I am a bit worried about elaborating as it might just confuse matters if you didn't understand it so far.
Anyway...
Extend TSprite to TAnimatedSprite which contain details on the animation properties : Timing, number of frames in anim, first frame, last frame, current frame etc. The TSprite assigned to the player is determined by the players currentstate : Walking, running, slashing, idle etc.
Therefore Player AnimState defines the players current state which sets the AnimatedSprite assigned to the player via the TSprite base class.


XxGuardianKnightxX(Posted 2009) [#6]
Alright I'll see what I can do...

Also, is there any way I can give back to the forums here? I don't want to keep asking questions and asking questions...sooner or later someone will get pissed at me for being a "noob" or whatever you call it. Not sure how I can contribute, considering I know next to nothing about coding.


Foppy(Posted 2009) [#7]
To keep it (relatively) simple you could not use the extra t_sprite type for the moment, and add a couple of fields to the t_player type for the image and the current frame. The extra fields could be:

- an image variable (TImage); this points to one of the animations that you loaded as global variables
- an int representing the current frame in the current animation

As the player moves, the int increases until the maximum frame is reached, at which stage it wraps around to 0. If the player changes direction, the image would be changed (it would then point to one of the other global images) and the frame counter would be reset to 0.

(Of course there can be situations where using separate sprite objects to represent the images of otherwise different types of objects is useful but if this is not such a case it might make things unnecessarily complicated.)

When that works you could add a "state" variable (for instance an int) to represent the current state: 0 = walking, 1 = standing. This state changes depending on whether the player is holding down movement keys. Based on this state, the above image is selected from different sets of images.


XxGuardianKnightxX(Posted 2009) [#8]
- an image variable (TImage); this points to one of the animations that you loaded as global variables


I notice "TImage" is referred to a lot by the BlitzMax community. Is there something special about that particular variable name? I tried typing it and it did not highlight itself, so I highly doubt the possibility of it being a built-in variable with BlitzMax, something I heard from a tutorial website.

Without further ado, here is the new 'player' type, used in the functional version of my code:
' Create the player type
Type t_player
	Field x_pos: Int' x_pos is an integer
	Field y_pos: Int' y_pos is an integer
	Field c_frame: Int' the current frame of the sprite
	Field c_state: spr_s' the current sprite (spr_s for now)
End Type

If I should replace "c_state" with "TImage then please let me know.

EDIT: I tried defining and calling functions the old BlitzBasic way, but it failed. My style of coding usually involves a very sparse main loop which mainly just calls the proper functions to do more complex actions such as updating the sprite, checking collisions, etc. and ideally I would update the animations/sprites in a function rather than in the main loop...that is just my style, that's all.

If you can tell me how to properly use functions in BlitzMax then I'm greatly appreciate it :)


Ginger Tea(Posted 2009) [#9]
Also, is there any way I can give back to the forums here? I don't want to keep asking questions and asking questions...sooner or later someone will get pissed at me for being a "noob" or whatever you call it. Not sure how I can contribute, considering I know next to nothing about coding.


right now you might not be able to answer questions another 'noob' would ask, but in time with enough coding hours under your belt you could

and in a way this here thread is apart of that giving back to the community, if people search the fourms in the future and this pops up and helps them then youve already helped (by asking the same questions they are) ;)


Foppy(Posted 2009) [#10]
I didn't explain it very clearly, but TImage is not the name of a variable itself, but a type of variable (like Int, Float...). Like this:

Global myImage:TImage = LoadImage("soldier.png")


I think you would need two distinct variables for storing a) the state and b) the image. So something like this:

Type t_player
   Field x:Int
   Field y:Int

   Field image:TImage
   Field frame:Int

   Field state:Int
End Type


About functions, you can add functions to types that are part of the type, called methods. Then once you create an instance of type t_player, you can call a method for that instance:

Global myImage:TImage = LoadImage("soldier.png")


Type t_player
   Field x:Int
   Field y:Int
   Field image:TImage

   Method init(pX:Int, pY:Int, pImage:TImage)
      ' the fields are known inside the method
      x = pX
      y = pY
      image = pImage
   End Method

   Method draw()
      DrawImage(image, x, y)
   End Method
End Type


Global player:t_player = New t_player

' set initial location and image
player.init(100,100,myImage)

player.draw()

Some more code is needed to make that a working example.

If you're new to Blitz Max perhaps starting with a game where the characters are drawn using a single image is easier; that way you can learn about program and type structure without having to worry about switching between images for different directions and steps in animations. In other words, one problem at a time. ;)


tonyg(Posted 2009) [#11]
You might want to review these :
Learning Object-Oriented Programming in Blitzmax
Beginners guide to BlitzMax

Learning 2D Game Programming With BlitzMax
which are all excellent and will help your understanding of Bmax functions, type methods and objects.


Derron(Posted 2009) [#12]
First of all you should consider using Floats over Integers when it comes to coordinates.

Blitzmax is able to draw images on fractions of screencoords.
So drawing from 0,0 | 0.5,0 | 1.0, 0 | ... will give a smoother kind of movement.


Next:
Spend your Types an update-Method - to divide your functionality to the things you will do when you want it - so update in mainloop only updates things, draw only does the things to draw images properly.


so (not tested, just typed in):

Type TBaseObject
	Field x:float, y:float
	

	function CreateBase:TBaseObject(x:float, y:float)
		local obj:TBaseObject = new TBaseObject
		obj.x = x
		obj.y = y
		
		return obj
	End function

	Method Draw() abstract
	Method Update() abstract
End Type

Type TAnimatedBaseObject extends TBaseObject
	field sprite:TImage
	field animPos:int=0
	field animCount:=1
	field animTime:int=0
	field animTimer:int 'every animTimer the animPos will increase

	function CreateAnimated:TAnimatedBaseObject(sprite:TImage, x:float, y:float, animPos:int=0, animCount:int=1, animTimer:int = 100)
		local obj:TAnimatedBaseObject = TAnimatedBaseObject
		obj.x = x
		obj.y = y
		obj.sprite = sprite
		obj.animPos = animPos
		obj.animCount = animCount
		obj.animTimer = animTimer		
		
		return obj
	End function

	Method UpdateAnimation()
		if self.animTime < MilliSecs()
			'increase position but only to the max defined by self.animCount
			'-1 to come to the base blitzmax defines (first stripframe is 0)
			self.animPos = Min(self.animPos+1, self.animCount-1)
			self.animTime = Millisecs() + self.animTimer
		endif
	End Method

	Method Draw() abstract
	Method Update() abstract

End Type


Type TPlayer extends TAnimatedBaseObject
	field name:string = "Player1"

	function Create:TPlayer(name:string="", image:TImage, x:float,y:float)
		local obj:TPlayer = new TPlayer
		obj.name = name
		obj.x = x
		obj.y = y
		'...
		'optional parameters or manual setting of variables like animcount etc.

		return obj
	end function

	Method Update()
		self.UpdateAnimation()
		'check for death
		'check for controllers
		' ...
	End Method

	Method Draw()
		DrawImage(self.sprite, self.x, self.y, self.animPos)
		DrawText(self.name, self.x, self.y-10)
		'... draw weaponeffects (bullets)... items ... other things ;D
	End Method
End Type

local Player1:TPlayer = TPlayer.create("HopeItWorks", 0.0, 50.5)


repeat
	'insert accumulator for update-limitation here
		Player.Update()
	'

	'insert accumulator for draw-limitation here
		cls
		Player.Draw()
	'
until KeyHit(Key_ESC)


Code wont work (no graphics initialisation and so on). also in general you would do the creations not the way i've done it now, I just didn't want to alter it after typing it in ;D


bye MB


XxGuardianKnightxX(Posted 2009) [#13]
If you're new to Blitz Max perhaps starting with a game where the characters are drawn using a single image is easier; that way you can learn about program and type structure without having to worry about switching between images for different directions and steps in animations. In other words, one problem at a time. ;)


I'm following the above tutorial "Learning 2D Game Programming With BlitzMax " right now. I remember scanning through it a few times before and being quite confused.

Also, the reason why the questions in this thread are so complex is because I figured I could get multiple things done in one thread, rather than asking question after question after question.

Again, an instinct I picked up to avoid people treating my like crap.

EDIT: I have coded a simple (not so simple for me haha) program to practice with, but something doesn't work.
'Load the cursor image (commented for now)
'img_cur = LoadImage("cursor.bmp")

'Set up graphics mode
Graphics 640,480,0
'Mask image
SetMaskColor 255,0,255

Type cursor
Field x:MouseX,y:MouseY
Field img: LoadImage ("cursor.bmp")
End Type


I get the error "expecting identifier but encountered string literal" when I try to compile it. Either it's there or it's here:
'draw cursor
DrawImage cursor.img,cursor.x,cursor.y



Foppy(Posted 2009) [#14]
I think it is complaining about the variable types of the fields;
Field x:MouseX,y:MouseY
Field img: LoadImage ("cursor.bmp")

should be
Field x:Float = MouseX()
Field y:Float = MouseY()
Field img:TImage = LoadImage ("cursor.bmp")

And before using a variable of type Cursor you have to create it, like this (also note that to avoid problems you shouldn't give the variable the same name as the type).
myCursor.cursor = new cursor

'draw cursor
DrawImage myCursor.img, myCursor.x, myCursor.y

(MouseX() and MouseY() will only be called once this way, upon creation of the Cursor type variable. To display the cursor at the new mouse location every frame you have to call the mouseX() and mouseY() functions every frame, assigning their values to the x and y of the cursor.)


XxGuardianKnightxX(Posted 2009) [#15]
Thanks very much...my test program works very well.

Also, "cursor.bmp" is a white plus-like sign I made in Paint. I noticed SetColor applies to it too, even though it is a loaded sprite. Why is this?

EDIT: Here is the complete code of my sample program in case anyone wants it or thinks they could learn from it (note that the music/sound and the cursor image will need to be supplied yourself.)
'Import the MaxMod module
Import MaxMod.ModPlayer' optional (but I like MODs!)
'Set the audio driver
SetAudioDriver("directsound")' optional

'Load the music
music = LoadSound("<filename.extension>")
'Play the music
PlaySound music

'Load the cursor image (commented for now)
'img_cur = LoadImage("<filename.extension>")

'Set up graphics mode
Graphics 640,480,0
HideMouse' hide the mouse

'Mask image
SetMaskColor 255,0,255

Type t_cursor
	Field x:Float = MouseX()
	Field y:Float = MouseY()
	Field img:TImage = LoadImage ("cursor.bmp")
	End Type

'Define string variable
hw$ = "Hello World!"
blk$ = "this is a flashing block"

'Main loop
While Not KeyDown(KEY_ESCAPE)
Cls
'Initiate random number generator
SeedRnd MilliSecs()
'RGB string
rgb$ = "RGB color "+R+","+G+","+B

R=Rnd(255)' random RED
G=Rnd(255)' random GREEN
B=Rnd(255)' random BLUE

'RndColor()

'Draw flashing, color-shifting rectangle
SetColor R,G,B
DrawRect 20,70,200,100
'Draw text
'SetColor 255,255,255' set color back to white (commented for now)
DrawText hw$,20,20
DrawText rgb$,20,40
'Set color to black for this string
SetColor 0,0,0
DrawText blk$,25,110

'set color back to random
SetColor R,G,B
'create t_cursor instance
cursor:t_cursor = New t_cursor
'draw cursor
DrawImage cursor.img,cursor.x,cursor.y

Flip

Wend
End

'Function definitions (commented for now)
'Function RndColor()
'	R=Rnd(255)' random RED
'	G=Rnd(255)' random GREEN
'	B=Rnd(255)' random BLUE
'End Function



Foppy(Posted 2009) [#16]
Yes, SetColor() affects different drawing commands, including DrawImage().

I just ran into that myself, I was drawing a red rectangle and noticed that an image I was displaying was also red.

So to avoid that, you have to call SetColor(255,255,255) before doing DrawImage(). (Resetting the drawing color to white.)


XxGuardianKnightxX(Posted 2009) [#17]
thank you for all the advice
you have to call SetColor(255,255,255) before doing DrawImage(). (Resetting the drawing color to white.)

Kinda figured that myself, and it works fine.

So I'm gonna go follow the 2D programming tutorial I started earlier, hopefully with success.

By the way, this thread is getting pretty long. The next time I post, it will most likely be in a more specific but similar topic. Do you want me to post it in a new thread, or keep posting here?


Foppy(Posted 2009) [#18]
I would start a new thread if the topic is different from "Player movement and sprites". :) Otherwise add to this one. The above discussion was still related.


XxGuardianKnightxX(Posted 2009) [#19]
I would start a new thread if the topic is different from "Player movement and sprites". :) Otherwise add to this one. The above discussion was still related.


Will-do...

Foppy, I like you. You're cool and helpful. Your name's based on that small, orange ball enemy from Earthbound, right?


Foppy(Posted 2009) [#20]
Unfortunately, no.. I was playing games on PC and c64 with a friend in 1992, and we were typing in hi-score names, and I said "you could use anything as a screen name, for instance Foppy". (I am Dutch, and didn't even know that it is actually a word in English!)


tonyg(Posted 2009) [#21]
I am Dutch, and didn't even know that it is actually a word in English!

Foppy, Does that mean that you don't now know that it's not a word in English ;-)


Foppy(Posted 2009) [#22]
Hey, that's strange, I thought it was in a dictionary. Perhaps it is a slang version of the word "foppish" then. :) ( http://idiomfind.com/slang-dictionary-f.htm )


tonyg(Posted 2009) [#23]
I know it's well off-topic but I love this sort of stuff.
Fop is thought to come from Foppe (pron Fop E). The origin is Old-English which is thought to come from Saxon Germany BUT is more closely linked to Frisian spoken by people coming from Friesland now part of Netherlands.
After all that... Foppy is likely to be a dutch/deutsch word.
<edit> Sorry... back to the spritey-stuff


Foppy(Posted 2009) [#24]
It is true that "Foppe" (pronounced foppuh) is a name in Friesland! Ok. ;)


Philip7(Posted 2009) [#25]
On a dutch forum this would be the moment to shout:

HEERENVEEN! Foppe de Haan!

But this isn't a dutch forum so sorry :)

sooner or later someone will get pissed at me for being a "noob"


Anyone complaining about noobs in the BlitzMax Beginners Area is a noob himself.


Czar Flavius(Posted 2009) [#26]
I've made some modifications to your code which you will hopefully find useful. Here are some comments:

If you want to use strict/superstrict mode, it means you have to declare all variables before using them. Variables are declared using Local, Global, Const or Field. The code in your first post would not compile because you did not properly declare your variables. For example, spr_n=Load.. should have been Local spr_n:TImage=Load.. For strings you can use the $ symbol, but you can also declare them using :String and then you don't need to do anything special afterwards. It is up to you which way to use.

Use variable names that are meaningful. When your program gets large you will find it difficult to remember what things such as blk$ are meant to be. I renamed it to block_text, which you can instantly recognise.

Notice I declared the variables required in your while loop, just before it starts. Because two of the texts do not change, I put them as Const. A Const is basically an unchanging variable, but the program would work just as well if you declared them using Local. Notice that color_text is Local because its value will change in the while loop.

SeedRnd needs to be called only once per program, so it is usually put near the start. Putting it inside a loop will mean it is caused several times per second, which is not good.

Watch your indentation. Notice that my while loop contents are tabbed inwards and makes for more readable code.

Although it can be handy for learners, it is not really helpful to comment every line of code, especially if the meaning is obvious. It can actually make the code harder to read then. For example, you will quickly remember that SetColor 255, 255, 255 is for white, and so on so you don't need to comment that.

I renamed t_cursor to TCursor. This makes no difference to the program but I am used to that kind of style so it made editing the program easier for me :) Plus it is consistant with the built in types (TImage, TList etc)

In your old code you were creating a new TCursor object each time the loop ran, as you can see the New is there. There is nothing "wrong" with doing it this way, but it is more efficient to have a single TCursor object, and simply change its values (eg x, y to the new mouse position) rather than destroy the old object and replace it with an entirely new object, just to change a value. Remember, the "cursor" variable is NOT a cursor object, it is a cursor object *holder*. Each time you give it a "New" command, it chucks out the old and makes a new cursor object.

I created two methods in the TCursor object, update() and draw(). Methods are like functions, but are used on a particular object. Whenever you type cursor.update() in your program (or whatever you call the holder variable), it will run the update method! When inside a method, you don't need to write "cursor.x" you can just put the field's name directly, "x", as methods know what objects they are for. So update() will change the cursor's x and y fields to new values for the current mouse position.

The draw() method is basically cut-and-paste of "DrawImage cursor.img,cursor.x,cursor.y" from the while loop into a method (with "cursor" removed inside for the same reasons above). This doesn't add any new functionality, it just makes your main code a bit more concise.

You will notice one of the "Fields" of the cursor type is listed as Global. Doing this is kind of like a normal Global variable, except it's a Global variable attached to a particular type. Unlike fields, which are unique to each object (you could have two cursor objects at the same time, with their own values - for two mice??), Global fields are the same for ALL objects of that type..

..In our simple program of having only 1 cursor it doesn't make much difference to have it as Global rather than Field. However, each time you use the LoadImage command, the program has to access data from the harddrive. So in your old code, every time you went through the while loop the image was being loaded again and again and again... this is not good. By putting it as a Global, we load it only once. If we ever destroy a cursor object and create a new one, the Global image would still exist in memory so no need to reload the image.

Without further ado, here is the modified code :)
SuperStrict

Graphics 640, 480, 0
HideMouse

SeedRnd MilliSecs() 


SetMaskColor 255, 0, 255


Type TCursor
	Global image:Timage = LoadImage("BLAHBLAHBLAH")
	Field x:Float
	Field y:Float	
	Method update()
		x = MouseX()
		y = MouseY()
	End Method
	
	Method draw()
		DrawImage image, x, y
	End Method
End Type


Local cursor:TCursor = New TCursor
Local red:Int, green:Int, blue:Int

Const welcome_text:String = "Hello World!"
Const block_text:String = "this is a flashing block"
Local color_text:String


While Not KeyDown(KEY_ESCAPE)
	Cls
	
	red = Rnd(255)
	green = Rnd(255)
	blue = Rnd(255)

	color_text = "RGB color " + red + "," + green + "," + blue

	'Draw flashing, color-shifting rectangle
	SetColor red, green, blue
	DrawRect 20, 70, 200, 100

	'Draw text
	SetColor 255, 255, 255
	DrawText welcome_text, 20, 20
	DrawText color_text, 20, 40

	SetColor 0, 0, 0
	DrawText block_text, 25, 110
	
	'set color back to random
	SetColor red, green, blue
	
	'update cursor position
	cursor.update()
	
	'draw cursor
	cursor.draw()
	
	Flip
	
Wend
End



XxGuardianKnightxX(Posted 2009) [#27]
Anyone complaining about noobs in the BlitzMax Beginners Area is a noob himself.

I concur :)

Anyways, I have an annoying problem with this current code I'm working on...Even though SetMaskColor is, I think, defined perfectly, it still displays a pink border around the image. This has happened before too!
' Simple Shooter Game
'*******************************
'*   coded by Nick Hagen       *
'*   on January 8th 2009       *
'*******************************

'******** Init Section *********

Graphics 640,480,0

'Mask images
SetMaskColor 255,0,255

'Define player type
Type t_player
	Field x_pos:Int
	Field y_pos:Int
	Field sprite:TImage = LoadImage ("gfx\sprites\ship_2.png")
End Type

'Create new spaceship object
player:t_player = New t_player

HideMouse()

'********* Main Loop *********

Repeat

	Cls
	player.x_pos = MouseX()
	player.y_pos = 420
	DrawImage player.sprite,player.x_pos,player.y_pos
	Flip
Until MouseHit(1)
End



Czar Flavius(Posted 2009) [#28]
Try LoadImage ("gfx\sprites\ship_2.png", MASKEDIMAGE)


XxGuardianKnightxX(Posted 2009) [#29]
My mistake...I got the pink color wrong. I corrected it and set it to 255,0,255 again and it works, even without MASKEDIMAGE.

EDIT: More trouble, this time with LoadAnimImage. I changed the input from mouse to keyboard, and made it so that the ship tilts left or right when moving left or right (3 images in a strip.)

I run it and it displays the left-tilted ship (frame 2) when it should not, and when you press right (not sure about left...doesn't do anything currently) the program crashes and gives the debug message "Unhandled Memory Exception Error." This is really annoying.

Anyways, I started with:
'Define player type
Type t_player
	Field x_pos:Int
	Field y_pos:Int
	Field sprite:TImage = LoadAnimImage ("gfx\sprites\ship_tilt.png",57,57,0,3)
	Field state:Int

	Method UpdateState()
		'Tilt left
		If KeyDown(KEY_LEFT) Then
			state = 2
			Else
			state = 1
		EndIf
		'Tilt right
		If KeyDown(KEY_RIGHT) Then
			state = 3
			Else
			state = 1
		EndIf	
	End Method
	
	Method DrawSelf()
		DrawImage sprite,x_pos,y_pos,state
	End Method		
	
End Type


And I think that's where I went wrong. The main loop is sparse, as per my usual coding style:
'********* Main Loop *********

Repeat

	Cls
	player.UpdateState()
	player.DrawSelf()
	Flip
Until MouseHit(1)
End



Czar Flavius(Posted 2009) [#30]
Run the program in Debug Mode, this will give much more helpful error messages. Plus it will tell you the exact line on which the program crashed (this will be very useful) and also lets you see the value of all variables.

There is a problem with the checks. Let's imagine the left key is down, but not the right key. The first check will set the state to 2, which is what we want. The second check will fail, as the right key is not down, therefore setting the state to 1. Which is not what we want.

Put state = 1 before the two If statements, and remove their Else parts. That should help things.


Philip7(Posted 2009) [#31]
Also, i believe 3 frames means from 0 to 2 so you need to lower all states by 1. You're probably getting the error because there is no frame 3 (which in fact would be the fourth frame).

Try:
DrawImage sprite, x_pos, y_pos, state-1

for a quick and dirty check.


XxGuardianKnightxX(Posted 2009) [#32]
Also, i believe 3 frames means from 0 to 2 so you need to lower all states by 1. You're probably getting the error because there is no frame 3 (which in fact would be the fourth frame).

Well as I do it, this is the only way that works:
LoadAnimImage ("gfx\sprites\ship_dir.png",57,57,0,3)

I don't get it...if the computer counts from zero, then how come the total number of frames is three and not two? I tried two and it didn't work, plus all my coding buddies (and me) have always put the total number of frames in "human" notation, rather than computer.

I have also gotten it to work now...but a few complications arose.
Method UpdateState()
		'Tilt left
		If KeyDown(KEY_LEFT) Then
			state = 1
		'Tilt right
			Else If KeyDown(KEY_RIGHT) Then
			state = 2
			Else
			state = 0
		EndIf	
	End Method


Method MoveCheck()
		'Move left
		If state = 1 Then
			x_pos = x_pos - 5
		'Move Right
		Else If state = 2 Then
			x_pos = x_pos + 5
		EndIf
	End Method

The program runs fine, but I noticed the left side is dominant (Game Maker gives the same problem.) What I mean by this is that sure, left key is left and right key is right, but if both keys are held down, then the ship will move left (state = 1.) This really annoys me for some reason, especially since the "proper" way games work is that when both keys are down, the ship stays still (state = 0.)


GaryV(Posted 2009) [#33]
Read up on the use of "and"?


Jesse(Posted 2009) [#34]
you can do something like this:


Method MoveCheck()
Method UpdateState()
		state = keydown(KEY_RIGHT)-keydown(KEY_LEFT)
	End Method

Method MoveCheck()
		x_pos = x_pos + 5*state
	End Method
	Method DrawSelf()
		DrawImage sprite,x_pos,y_pos,1+state
	End Method		



tonyg(Posted 2009) [#35]
I don't get it...if the computer counts from zero, then how come the total number of frames is three and not two? I tried two and it didn't work, plus all my coding buddies (and me) have always put the total number of frames in "human" notation, rather than computer.


Loadanimimage has the 'first frame' (in your example frame 0) and how many frames (in your example 3 : 0, 1, 2)


Czar Flavius(Posted 2009) [#36]
If state = 1 Then
	x_pos = x_pos - 5
Else If state = 2 Then
	x_pos = x_pos + 5
EndIf


This does not work because state cannot be both 1 and 2 at the same time.


Czar Flavius(Posted 2009) [#37]
Consider

'Somewhere
Global leftkey:Int = False, rightkey:Int = False
Const leftstate:Int = 1, rightstate:Int = 2, normalstate:Int = 0
Const movespeed:Int = 5

Method UpdateState()
	If KeyDown(KEY_LEFT) Then
		leftkey = True
	Else
		leftkey = False
	EndIf

	If KeyDown(KEY_RIGHT) Then
		rightkey = True
	Else
		rightkey = False
	EndIf
	
	state = normalstate
	
	If leftkey Then state = leftstate
	If rightkey Then state = rightstate
	If leftkey And rightkey Then state = normalstate
	
End Method

Method MoveCheck()
	Select state

		Case normalstate
		'do nothing

		Case leftstate
		x_pos = x_pos - movespeed

		Case rightstate
		x_pos = x_pos + movespeed
	End Select
End Method


(Untested)


Jesse(Posted 2009) [#38]
@Czar Flavius, what is the advantage of doing it your way versus the way I did it? It does all that with fewer instructions. it produces 0 when no keys are pressed or when both keys are pressed and add 5 when the right key is pressed and subtract 5 when the left key is pressed.
I updated my post above to work with his code.


Czar Flavius(Posted 2009) [#39]
I was just posting alternatives :)

My way is more expandable to handle nore than two input keys, but yes for only two your way is more concise.