Looking closer at raylib's 2d capabilities let get some stuff set up
from enum import Enum # these are the different movement states for the player class Moving(Enum): NOT = 0 UP = 1 DOWN = 2 LEFT = 3 RIGHT = 4 DONE = 5
A small "Enum" class allows us to create a simple state machine this will help up keep track of what is happening with our player character.
def getTxFromTile(source, x:int , y:int): ii = rl.ImageFromImage(source, [x*64,y*64,64,64]) tx = rl.LoadTextureFromImage(ii) rl.UnloadImage(ii) return tx
cutting the "sprites" that I want out of an image, if fairly straight forward
sheet = rl.LoadImage(b"data/sokoban_tilesheet.png") plTx = [ getTxFromTile(sheet,0,5),getTxFromTile(sheet,1,5),getTxFromTile(sheet,2,5), getTxFromTile(sheet,3,5),getTxFromTile(sheet,4,5),getTxFromTile(sheet,5,5), getTxFromTile(sheet,0,7),getTxFromTile(sheet,1,7),getTxFromTile(sheet,2,7), getTxFromTile(sheet,3,7),getTxFromTile(sheet,4,7),getTxFromTile(sheet,5,7), ] rl.UnloadImage(sheet)
Notice that once the textures are created and uploaded to the GPU you don't need to keep hold of the image...
Ideally I should have made a class of the player chararcter but as there is just one its often easiest just treating it as a special case, we'll look later a class to handle other entities in the game late - ie crates!
def updatePlayer(laFr, lisMoving, lmoveCount): f = 55 # if moving update the move counter if lisMoving != Moving.DONE and lisMoving != Moving.NOT: f += laFr lmoveCount -= 1 if lmoveCount == 0: lisMoving = Moving.DONE
This is the first part of updating the player, we have a move counter as we want to restrict the player to a grid of 64x64 pixel squares. A single press of a movement key will move the player till the move counter runs out as it reaches the next square
# if not moving check for movement key presses if lisMoving == Moving.DONE or lisMoving == Moving.NOT: lisMoving = Moving.DONE if rl.IsKeyDown(rl.KEY_UP): lisMoving = Moving.UP lmoveCount = 64 if rl.IsKeyDown(rl.KEY_DOWN): lisMoving = Moving.DOWN lmoveCount = 64 if rl.IsKeyDown(rl.KEY_RIGHT): lisMoving = Moving.RIGHT lmoveCount = 64 if rl.IsKeyDown(rl.KEY_LEFT): lisMoving = Moving.LEFT lmoveCount = 64
If we're not moving just check for key presses, notice the move counter gets set as does a direction.
if lisMoving == Moving.DOWN: playerPos.y += 1 if lisMoving == Moving.UP: playerPos.y -= 1 f += 3 if lisMoving == Moving.RIGHT: playerPos.x += 1 f += 22 if lisMoving == Moving.LEFT: playerPos.x -= 1 f += 25 return f, lisMoving, lmoveCount
finally depending on our movement state we can alter our position, notice the animation frame is off set depending on our direction. also note that this function is returning multiple values, which as mentioned might have been better as members of a class.
Here's the complete code for our simple animated sprite
#!/usr/bin/env python3 from raylib.static import rl, ffi from raylib.colors import * from enum import Enum # these are the different movement states for the player class Moving(Enum): NOT = 0 UP = 1 DOWN = 2 LEFT = 3 RIGHT = 4 DONE = 5 def updatePlayer(laFr, lisMoving, lmoveCount): f = 55 # if moving update the move counter if lisMoving != Moving.DONE and lisMoving != Moving.NOT: f += laFr lmoveCount -= 1 if lmoveCount == 0: lisMoving = Moving.DONE # if not moving check for movement key presses if lisMoving == Moving.DONE or lisMoving == Moving.NOT: lisMoving = Moving.DONE if rl.IsKeyDown(rl.KEY_UP): lisMoving = Moving.UP lmoveCount = 64 if rl.IsKeyDown(rl.KEY_DOWN): lisMoving = Moving.DOWN lmoveCount = 64 if rl.IsKeyDown(rl.KEY_RIGHT): lisMoving = Moving.RIGHT lmoveCount = 64 if rl.IsKeyDown(rl.KEY_LEFT): lisMoving = Moving.LEFT lmoveCount = 64 if lisMoving == Moving.DOWN: playerPos.y += 1 if lisMoving == Moving.UP: playerPos.y -= 1 f += 3 if lisMoving == Moving.RIGHT: playerPos.x += 1 f += 22 if lisMoving == Moving.LEFT: playerPos.x -= 1 f += 25 return f, lisMoving, lmoveCount def getTxFromSheet(source, x:int , y:int): ii = rl.ImageFromImage(source, [x*64,y*64,64,64]) tx = rl.LoadTextureFromImage(ii) rl.UnloadImage(ii) return tx #// Initialization #//-------------------------------------------------------------------------------------- screenWidth = 800; screenHeight = 450; rl.SetConfigFlags(rl.FLAG_MSAA_4X_HINT| rl.FLAG_WINDOW_RESIZABLE) # Enable Multi Sampling Anti Aliasing 4x (if available) rl.InitWindow(screenWidth, screenHeight, b"step 2") sheet = rl.LoadImage(b"data/sokoban_tilesheet.png") # load in all the tiles... tiles = [] for y in range(0,8): for x in range(0,11): tiles.append(getTxFromSheet(sheet,x,y)) rl.UnloadImage(sheet) frame = 0 aFr = 0 isMoving = Moving.NOT moveCount = 0 rl.SetTargetFPS(60) playerPos = ffi.new("struct Vector2 *",[ screenWidth/2, screenHeight/2]) while not rl.WindowShouldClose(): #// Detect window close button or ESC key #// Update #//---------------------------------------------------------------------------------- frame+=1 if not(frame % 6): # every 6th frame aFr+=1 # increment player frame if aFr == 3: # does it need to wrap (return to zero) aFr=0 # update the player tells us the animation frame for the player # movement status and the count through the move playerFrame, isMoving, moveCount = updatePlayer(aFr, isMoving, moveCount) #//---------------------------------------------------------------------------------- #// Draw #//---------------------------------------------------------------------------------- rl.BeginDrawing() rl.ClearBackground(RAYWHITE) # draw the player rl.DrawTexture(tiles[playerFrame], int(playerPos.x), int(playerPos.y), WHITE) if rl.IsKeyDown(rl.KEY_SPACE): i=0 for y in range(0,8): for x in range(0,11): rl.DrawTexture(tiles[i], x*66, y*48, [255,255,255,128]) rl.DrawText(rl.TextFormat(b"i: %i",ffi.cast("int", i)), x*66, y*48, 20, BLACK) i+=1 rl.DrawFPS(10, 10) #rl.DrawText(f"Frame: {int(frame)}".encode('UTF-8') , 10, 28, 20, MAROON) rl.DrawText(rl.TextFormat(b"Frame: %i",ffi.cast("int", frame)), 10, 30, 20, MAROON) rl.DrawText(rl.TextFormat(b"a fr : %i",ffi.cast("int", aFr)), 10, 60, 20, MAROON) rl.EndDrawing() for i in tiles: rl.UnloadTexture(i)