Paulo Laureano Because coding is fun!

Example "drawing sprites & collision" in Python


This example uses python 3 with a Raylib (https://www.raylib.com) wrapper (https://pypi.org/project/raylib/) that uses CFFI API static bindings (this is the fastest approach, and keeps the code as close as possible to the original C, which I feel is the best approach when using a C library).

Create a folder called "resources" (in the same directory as your code) and place the two images (provided bellow) there.

hero_shipufo1


Here is the Python sample code…



from raylib.static import *
from ctypes import *

screenWidth = 800
screenHeight = 600

InitWindow(screenWidth, screenHeight, b"sprites & collision example")

SetTargetFPS(60)

# hero ship
speed = 4.0
ship_size_width = 50
ship_size_height = 75

ship_position={'x': screenWidth/2.0, 'y': screenHeight-ship_size_height}
ship_rectangle = { 'x': ship_position["x"], 'y': ship_position["y"],'width': ship_size_width, 'height': ship_size_height }
image_hero_ship = LoadImage(b"resources/hero_ship.png")

ImageResize(ffi.addressof(image_hero_ship), ship_size_width, ship_size_height)

hero_ship=LoadTextureFromImage(image_hero_ship)


#bad dude ship
baddude_speed = 4.0
baddude_ship_size_width = 100
baddude_ship_size_height = 50

baddude_ship_position={'x': screenWidth/2.0, 'y': 0}
baddude_ship_rectangle = { 'x': baddude_ship_position["x"], 'y': baddude_ship_position["y"], 'width': baddude_ship_size_width, 'height': baddude_ship_size_height }
image_baddude_ship = LoadImage(b"resources/ufo1.png")

ImageResize(ffi.addressof(image_baddude_ship), baddude_ship_size_width, baddude_ship_size_height)

baddude_ship=LoadTextureFromImage(image_baddude_ship)



# detect collision
def my_check_collision_recs(rec1,rec2):
if rec1["x"] < rec2["x"] + rec2["width"] and rec1["x"] + rec1["width"] > rec2["x"] and rec1["y"] < rec2["y"] + rec2["height"] and rec1["y"] + rec1["height"] > rec2["y"]:
return 1
else:
return 0



while not WindowShouldClose():

# control the heroship
if IsKeyDown(KEY_RIGHT):
ship_position["x"] += 2.0
ship_rectangle["x"] += 2.0
if IsKeyDown(KEY_LEFT):
ship_position["x"] -= 2.0
ship_rectangle["x"] -= 2.0
if IsKeyDown(KEY_UP):
ship_position["y"] -= 2.0
ship_rectangle["y"] -= 2.0
if IsKeyDown(KEY_DOWN):
ship_position["y"] += 2.0
ship_rectangle["y"] += 2.0

BeginDrawing()

ClearBackground(RAYWHITE)
DrawTexture(baddude_ship, int(baddude_ship_position["x"]), int(baddude_ship_position["y"]), LIGHTGRAY)
DrawTexture(hero_ship, int(ship_position["x"]), int(ship_position["y"]), LIGHTGRAY)


if my_check_collision_recs(ship_rectangle, baddude_ship_rectangle):
DrawText(b"BOOM", 10, 10, 20, LIGHTGRAY)


EndDrawing()
CloseWindow()


Example "drawing sprites & collision" in Rust

Create a folder called "resources" (in the same directory as your code) and place the two images (provided bellow) there.

hero_shipufo1


Here is the Rust code…


extern crate raylib;
use raylib::prelude::*;
use raylib::prelude::KeyboardKey::*;
use raylib::ffi::CheckCollisionRecs;

fn main() {
let screen_width = 800.0;
let screen_height = 600.0;

let (mut rl, thread) = raylib::init()
.size(screen_width as i32, screen_height as i32)
.title("Simple shooter")
.build();

//hero ship
let speed = 4.0;
let ship_size_width = 50;
let ship_size_height = 75;

let mut ship_position = Vector2 {
x: screen_width / 2.0,
y: screen_height - ship_size_height as f32,
};

let mut ship_rectangle = Rectangle {
width: ship_size_width as f32,
height: ship_size_height as f32,
x: ship_position.x,
y: ship_position.y,
};

// graphics load hero ship
let mut image_hero_ship = Image::load_image("resources/hero_ship.png");

let i=image_hero_ship.as_mut().unwrap();
Image::image_resize(i, 50, 75);

let mut hero_ship = rl.load_texture_from_image(&thread, i);
let hs=hero_ship.as_mut().unwrap();

//bad dude ship
let baddude_speed = 4.0;
let baddude_ship_size_width = 75;
let baddude_ship_size_height = 50;

let mut baddude_ship_position = Vector2 {
x: 0.0,
y: 20.0 + baddude_ship_size_height as f32,
};

let mut baddude_ship_rectangle = Rectangle {
width: baddude_ship_size_width as f32,
height: baddude_ship_size_height as f32,
x: baddude_ship_position.x,
y: baddude_ship_position.y,
};

// graphics load bad dude ship
let mut image_baddude_ship = Image::load_image("resources/ufo1.png");

let bi=image_hero_ship.as_mut().unwrap();
Image::image_resize(bi, baddude_ship_size_width, baddude_ship_size_height);

let mut baddude_ship = rl.load_texture_from_image(&thread, bi);
let bs=baddude_ship.as_mut().unwrap();


rl.set_target_fps(60); // Set our game to run at 60 frames-per-second

while !rl.window_should_close() {
// beging drawing operations
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::BLACK);

// control the hero ship
if d.is_key_down(KEY_RIGHT)
&& ship_position.x < screen_width as f32 - ship_size_width as f32
{
ship_position.x += speed;
}
if d.is_key_down(KEY_LEFT) && ship_position.x > 0.0 {
ship_position.x -= speed;
}

if d.is_key_down(KEY_UP) && ship_position.y > 30.0 {
ship_position.y -= speed;
}
if d.is_key_down(KEY_DOWN)
&& ship_position.y < screen_height as f32 - ship_size_height as f32
{
ship_position.y += speed;
}

// lets make the bad dude move
if baddude_speed as i32 > 0
&& baddude_ship_position.x as i32 + (baddude_speed as i32) < screen_width as i32
{
baddude_ship_position.x += baddude_speed
} else {
baddude_ship_position.x = 0.0 - baddude_ship_size_width as f32;
}

// draw the baddude ship
d.draw_texture(
&bs,
baddude_ship_position.x as i32,
baddude_ship_position.y as i32,
Color::LIGHTGRAY,
);

// keep the collision rectangle up to date on baddude ship
baddude_ship_rectangle.x = baddude_ship_position.x;
baddude_ship_rectangle.y = baddude_ship_position.y;

// draw the hero ship
d.draw_texture(
&hs,
ship_position.x as i32,
ship_position.y as i32,
Color::LIGHTGRAY,
);

// keep the collision rectangle up to date on hero ship
ship_rectangle.x = ship_position.x;
ship_rectangle.y = ship_position.y;

// any collisions????
if my_check_collision_recs(ship_rectangle, baddude_ship_rectangle) {
// BOOOOM
d.draw_text("BOOM", 500, 10, 20, Color::LIGHTGRAY);
}

//scores...
d.draw_text("0", 10, 10, 20, Color::LIGHTGRAY);

}
}



pub fn my_check_collision_recs(rec1: Rectangle, rec2: Rectangle) -> bool {
if rec1.x < rec2.x + rec2.width &&
rec1.x + rec1.width > rec2.x &&
rec1.y < rec2.y + rec2.height &&
rec1.y + rec1.height > rec2.y
{
return true;
}
return false;
}

Learn to code (Octopus game in Blitzmax) in a few minutes




So, you want to learn to code/program computers? Fancy the ideia of making your own games one day? Cool. Let's learn.

What you need to know in order to interpret the demo game by looking at the source code:


0 - Install Blitzmax compiler and IDE



Download Blitxmax from https://blitzmax.org for your computer. Install it and run MaxIDE.


1 - Data types



The formula to declare a data type is:


scope name:type


Scope -> can be "Global" or "Local". It determined if your variable is available to all the project code or just for the block it was defined in.

Name -> whatever you want to call it, usually describes the content…

type -> in this game we use "int" (integer) for numeric values (like 1, 2, 3), "long" for some real big numbers, "float" for floating point numbers (like 1.5, 2.7, etc) and "string" to hold strings of characters (like "hello world").

So, if you see something like:


Global AuthorName:string


It means it is available this variable is available of the entire project (Global), it is called "AuthorName" (the variable name) and is of type "string" (holds a bunch of chraracters.

After a variable is declared, it can be assigned a value anywhere on your code (as long as it is in scope):


AuthorName="Paulo Laureano"


You may also assign the value on the same line you declare the variable:


Global AuthorName:string="Paulo Laureano"



A few others examples:


Local drink:string="soda"
Local bottle_capacity_in_liters:float=0.33
Local bottles:int=6


2 - Arrays



You can have several copies of a variable as an Array:


Local drink:string[5]
Local bottle_capacity_in_liters:float[5]
Local bottles:int[5]


…instead of declaring one variable in each of the previous lines, we are declaring an Array of 5 variables per line. These could then be assigned values:


drink[0]="coke"
drink[1]="soda"
drink[2]="water"
drink[3]="beer"
drink[4]="wine"


Note that Arrays start at 0 (zero), and end at the declared size minus 1.
Any data type can be used in an Array.


3 - Functions



A function is a block of code, that can be called by name, receive parameters and return values. Sounds complicated? Not really. Just bear with me.

Imagine a sode that does the following:


Local x:float
Local y:float
Local z:float

x = 8 + 4
x = x *10
x = x - 1

y = 24 + 4
y = y * 10
y = y - 1

z = x + y


So we want:

- x to be equal to 8 + 4
- x to be multiplied by 10
- x value to be decremented by 1

And then we want

- y to be equal to 24 + 4
- y to be multiplied by 10
- y value to be decremented by 1

And finally, we want to add the values of x and y…

Notice how similar the first two steps are. Can we create a function that does the same operations receiving 8 or 24 as a parameter and return the final value?

Sure, the formula is:

Function name:returns_datatype(receives_parameters)

End funtion

"returns_datatype" and "receives_parameters" are both optional (you can have functions that return nothing or receive nothing). So back to our example:


Function calculate:float(value:float)
local k:float

k = value + 4
k = k *10
k = k - 1

return k
End function

Local z:float = calculate(8) + calculate(24)


… does exactly the same thing as the previous code, and we end up with the same value in "z". Think of functions as a way to not repeat the same code, and to reuse code you already written. The less code, the less bugs, and that is a good thing. Functions rock!


4 - Structures / classes (defining new data types / associate code to manipulate the data)



You can create new data types yourself, the formula is:


Type name
fields

methods
End type


"Methods" are exactly like "functions" but they work inside the data type. "Fields" are exactly like variables, but the live inside the datatype. Sounds complex? Maybe, until you use it:


Type person
field name:string
field age:int
field gender:int
field height:float

method describe()
print "This person is called " + name + ". It is " + age + " years old, " + get_gender()
+ " and " + height + "feet tall"
End method


method get_gender:string()
if gender=0 return "male"
if gender=1 return "female"
end method
End type

Local husband:person= new person
Local wife:person= new person

husband.name="John"
husband.age=49
husband.gender=0
husband.height=5.7

wife.name="Mary"
wife.age=32
husband.gender=1
husband.height=6.5

husband.describe()
wife.describe()



Grouping variables (data) and related methods (functions) is damn useful to avoid confusion as your programs get bigger and more complex.


5 - loops



You can loop thru code in several ways:


For local value:int=1 to 10
print value
next


Or loop while a certain condition is true


while game_running = true
… do stuff until game_running no longer true …
wend


These are the two loops you will encounter in the sample code.


6 - Conditions



Sometimes portions of your code should only be executed is a certain condition is met:


if something=value do_this


Or


If something=value Then
do_this
and_that
End if


Or you can go with


If something=value Then
do_this
and_that
Else
do_something_else
just_because_condition_was_not_met
End if



7 - Graphics / cls / flip



When your program uses graphics mode you have to start by setting the screen resolution and define if the program runs full screen or in a window


Graphics 800,600,0


… would run the program on a 800 wide by 600 tall window. In the sample game you have the example of running full screen with the highest available resolution.

All graphics applications need a loop with cls and flip:


' This is loops several times a second depending on the refresh rate of your monitor
While game_running
cls

' your code goes in here

flip
wend


"cls" is used to clear the screen every time you draw a new frame. "Flip" draws whatever you have on your loop into the screen (your drawing operations in reality happen at a backbuffer, and it is flipped when that command is issued).



8 - Sprites, sound & incbin



In order for your graphics and sounds to be included in the main binary you may use "Incbin". You must do it in to steps:


Incbin "path/to/your/file"


And afterwards to load the graphics/sound to your game use:


Global sprite:TImage=LoadImage("incbin::path/to/your/file")


For graphics…


Global noise:TSound=LoadSound("incbin::path/to/your/file")


For sounds…

To display images, once loaded:


DrawImage( sprite, x, y )


To play sounds, once loaded


PlaySound( noise )




That's it. You are ready to dive in the "Octopus" source code: All platforms