Wednesday, October 25, 2023

Dungeon action

 This is a continuation of the "Procedural dungeon", it will contain a guide to make mainly: collectable items for an inventory (with a HUD), one enemy, and it will lead the path to finish the mini-game with an in-game shop (no outside currency at all).

Before going into the new stuff for the mini-game, a few bug fixing have to be made upon the previous job:

0 - Write the tile sizes for the map properly,
`var map_size = Vector2(960/16,544/16) # Display | Window Size / tile size`

1 - To avoid the player moving while the talking text is being displayed, a new variable
for the player script is added, ` export var canMove = true `. In addition, now the NPC
script will modify that accordingly:
```
func set_hiden(value):
    talk = value
    # Show/Hide dialogue
    $CanvasLayer.visible = talk
    if talk == true :
        get_parent().get_node("Player").canMove = false
    else :
        get_parent().get_node("Player").canMove = true
```

2 - The spawning problem in the player and NPC:
This adds extra complexity to the game and code, due to that and the nature of this project I've only included the most obvious cases, but there are more. To put it simply, we're only going to check if a background (dark pit) exists in the spawning position [If you want to do an exercise, check if characters overlap to add that case].

Tip: Add the objects you want to spawn into a group, so you can automatize (help in the screenshot).
 


```
func add_characters():
    var pos = Vector2()     # Spawn position
    for obj in get_tree().get_nodes_in_group("groupSpawn"):
        var spawn = false
        pos = spawn(pos)
        while canSpawn(pos) == false:
            pos = spawn(pos)
        obj.global_position = pos


func canSpawn(char_pos):
    # Check if the character is spawning in a good tile or not
    var cell_coordinates = get_node("Background").world_to_map(char_pos)
    var cellid = get_node("Background").get_cellv(cell_coordinates)
    if cellid == -1 :
        true
    else :
        false
    
func spawn(pos):
    # Screen size HEIGHT WEIGHT
    var HEIGHT = get_viewport().get_visible_rect().size.y
    var WEIGHT = get_viewport().get_visible_rect().size.x
    var rng = RandomNumberGenerator.new()
    rng.seed = randi()
    pos = Vector2(rng.randi_range(0,WEIGHT) , rng.randi_range(0,HEIGHT) )
    return pos
```


After those fixes, we need the base for the HUD ready to show how many items we have.
We've also added a Dictionary structure here to track the objects in the inventory.
 


```
extends CanvasLayer

var inventory : Dictionary = {
    "item0Number" : 0,
    "item1Number" : 0,
}
```


Now, since I want to learn new ways, I will follow this method for collectables (Using the procedurally generated tiles from the previous step) and their current position in the tilemap. Like here, https://youtu.be/1Fplm1Mkxb8
Assets for this part: https://gfragger.itch.io/magic-market

TIP: Change the cell name, so you can reference it in your script, check these 2 images:
    1 - Go to your tileset Node, and click on TileMap, then on Tileset
    2 - Select the tile you want to rename and go to TileSetEditorContext


Add variables for the collectables in your script dungeon2D.gd
 
```
onready var collectibles : TileMap = $Collectibles
var collectibles_caps = Vector2(1, 0.5)
var collectibles_chance = 10 # percent out of 100 that an item will be placed
```

And add the methods to procedurally generate them, in the same script. We will also make the collectable tracking process in this script. You can see the changes in the code for the dungeon2D.gd script methods below:
 
```
func _ready():
    randomize()
    noise = OpenSimplexNoise.new()
    noise.seed = randi()
    noise.octaves = 1.0
    noise.period = 12
#    noise.persistence = 0.7
    make_grass_map()
    make_road_map()
    make_enviroment_map()
    make_background()
    make_collectibles_map()
    $HUD/Label1.text = str(0)
    $HUD/Label2.text = str(0)
    add_characters()
    
func _physics_process(delta):
    check_player_clear_collectibles()
    
# Place random rocks on stone, or flowers on dirt
func make_collectibles_map():
    for x in map_size.x:
        for y in map_size.y:
            var  a = noise.get_noise_2d(x,y)
            if a < collectibles_caps.x and a > collectibles_caps.y:
                var chance = randi() % 100
                if chance < collectibles_chance:
                    collectibles.set_cell(x,y,1) # item1Number
            else :
                var chance = randi() % 100
                if chance < collectibles_chance:
                    collectibles.set_cell(x,y,0) # item2Number
# If player is on top of rock or flower clear item
func check_player_clear_collectibles():
    var cell_coordinates = collectibles.world_to_map($Player.global_position)
    var cellid = collectibles.get_cellv(cell_coordinates)
    if cellid >-1 :
        if collectibles.tile_set.tile_get_name(cellid) == "item0Number":
            $HUD/.inventory["item0Number"] += 1
            $HUD/Label1.text = str($HUD/.inventory["item0Number"])
        elif collectibles.tile_set.tile_get_name(cellid) == "item1Number":
            $HUD/.inventory["item1Number"] += 1
            $HUD/Label2.text = str($HUD/.inventory["item1Number"])
        collectibles.set_cellv(cell_coordinates, -1)
    
```
WARNING: Don't forget to add the objects in your map in the proper transform_position, so the grid and global coordinates are the same for both. Otherwise, if you want them to be collected or to chase you. They won't have the same position, and it won't be as you expect. (This has to be done in both, the main scene and your object scene. So be careful when editing)
> The cell size of the objects can also create weird results if they are not resized accordingly

Example in the screenshot:
(With a distance in the origin grid map, the global_position 0,0 in both of them will look like this).

"Look!! A dragon!! It comes after our treasure!!"

Time to make an enemy for our game. A new NPC scene, with different script, movements, and actions. For now, the dragon will only chase us (we can add more features later).
Example, to visualize, here: https://youtu.be/1e_dmDW73IQ?t=395
Assets: https://theartofnemo.itch.io/free-rpg-monsters-dragon-evolutions

Code to make the dragon chase us (this is the same code you would use if you want a companion NPC to follow you).
Obviously, this code is for the Dragon script, with its own node (you can copy the other NPC and add this code as a way to test it)

```
extends KinematicBody2D

onready var speed = get_parent().get_node("Player").speed

func _physics_process(delta):
    var velocity = Vector2.ZERO
    var direction = get_parent().get_node("Player/Area2D/CollisionShape2D").global_position
    velocity = self.global_position.direction_to(direction) * speed
    velocity = move_and_slide(velocity)
```

The next part should be the shop. However, to keep these entries short enough, this will be in the next entry (Hopefully that's the last one of this procedural mini-dungeon project).

Sunday, October 22, 2023

Procedural dungeon

To make a break from the hockey game, so I don't burn out, I decided to try another item of my wanted To-Do list. In other words, this entry is (as the title says) for making a dungeon with procedural generation.

First we need a basic idea of what we are doing, so here are two definitions:

About the topic at hand:
https://en.m.wikipedia.org/wiki/Procedural_generation

And about the selection made (which is one that requires less resources than others while still being good enough):
https://en.m.wikipedia.org/wiki/Simplex_noise and https://docs.godotengine.org/en/3.1/classes/class_opensimplexnoise.html

To put it simply, we are going to make an algorithm that follows a path to visit all the tiles in the screen, meanwhile, it will be filling them (or not) according to the parameters given. You can see the video explanation I took as a practical example for the base of today's minigame:
https://youtu.be/SBDs8hbs43w?si=C5VS1iVUUF-1mRhA And later we will add some features to interact with.

Procedural code for generating the dungeon, script attached to the main node of the scene:
 
 
 
 ´´´
extends Node2D

var noise
var map_size = Vector2(80, 60)
var grass_cap = 0.5
var road_caps = Vector2(0.3, 0.05)
var enviroment_caps = Vector3(0.4, 0.3, 0.04)

func _ready():
    randomize()
    noise = OpenSimplexNoise.new()
    noise.seed = randi()
    noise.octaves = 1.0
    noise.period = 12
#    noise.persistence = 0.7
    make_grass_map()
    make_road_map()
    make_enviroment_map()
    make_background()
    add_characters()
    
func make_grass_map():
    for x in map_size.x:
        for y in map_size.y:
            var a = noise.get_noise_2d(x,y)
            if a < grass_cap:
                $Grass.set_cell(x,y,0)
               
    $Grass.update_bitmask_region(Vector2(0.0, 0.0), Vector2(map_size.x, map_size.y))
    
func make_road_map():
    for x in map_size.x:
        for y in map_size.y:
            var a = noise.get_noise_2d(x,y)
            if a < road_caps.x and a > road_caps.y:
                $Roads.set_cell(x,y,0)
    $Roads.update_bitmask_region(Vector2(0.0, 0.0), Vector2(map_size.x, map_size.y))
    
func make_enviroment_map():
    for x in map_size.x:
        for y in map_size.y:
            var a = noise.get_noise_2d(x,y)
            if a < enviroment_caps.x and a > enviroment_caps.y or a < enviroment_caps.z:
                var chance = randi() % 100
                if chance < 2:
               
                    var num = randi() % 4
                    $Enviroment.set_cell(x,y, num)
               
               

func make_background():
    for x in map_size.x:
        for y in map_size.y:
            if $Grass.get_cell(x,y) == -1:
                if $Grass.get_cell(x,y-1) == 0:
                    $Background.set_cell(x,y,0)
               
    $Background.update_bitmask_region(Vector2(0.0, 0.0), Vector2(map_size.x, map_size.y))
               

func add_characters(): # Needs a few touches to avoid spawning in pits or colliders
    # Screen size HEIGHT WEIGHT
    var HEIGHT = get_viewport().get_visible_rect().size.y
    var WEIGHT = get_viewport().get_visible_rect().size.x
    
    var rng = RandomNumberGenerator.new()
    rng.seed = randi()
    $Player.global_position = Vector2(rng.randi_range(0,WEIGHT) , rng.randi_range(0,HEIGHT) )
    $NPC.global_position = Vector2(rng.randi_range(0,WEIGHT) , rng.randi_range(0,HEIGHT) )
 
```

 
For those who have problems with the tiles collision, they can be edited following the image bellow:

1- Go to the TileMap node (You can do so in the scene section)

2- Click on the TileMap resource (on the inspector panel)

3 - Click on the TileMap Image (The one that appears over the debugger section)

4 - Go to your desire Tiles and modify whatever you need.


Once we have our terrain, we can start digging about the interactions with it. To do that, we want a character controlled by us, so here's the basic code for it:

```
extends KinematicBody2D

class_name Player

export (int) var speed = 100

# onready var anim = $AnimationPlayer
# onready var animTree = $AnimationTree
# onready var animTree_playback = $AnimationTree.get("parameters/playback")

var velocity = Vector2()

func get_input():
    velocity = Vector2.ZERO
    if Input.is_action_pressed("move_right"):
        velocity.x += 1
    if Input.is_action_pressed("move_left"):
        velocity.x -= 1
    if Input.is_action_pressed("move_down"):
        velocity.y += 1
    if Input.is_action_pressed("move_up"):
        velocity.y -= 1
    velocity = velocity.normalized() * speed

func _physics_process(_delta):
    get_input()
    velocity = move_and_slide(velocity)

```

After being able to walk around, It's time to add an NPC to interact with:
 
 
 ```
extends KinematicBody2D

var  talk = false setget set_hiden

func _process(_delta):
    if Input.is_action_just_pressed("ui_accept"):
        var player = get_parent().get_node("Player/Area2D")
        if get_node("Area2D").overlaps_area(player):
            self.talk = !talk


func set_hiden(value):
    talk = value
    # Show/Hide dialogue
    $CanvasLayer.visible = talk
```

 
You could also make that an RPG turn based combat, making the conversation a transition to this scene if you want:
https://youtu.be/ifXGvlAn0bY?si=1KjngnMeaoJFfnfS (code here: https://github.com/jontopielski/Turn-Based-Combat )

Finally, there are a few things more, like the HUD to let the player see inventory and other "little" stuff. But, that's out of the scope of this entry, besides including those explanations would make this even longer. I will make the HUD, a shop, an enemy, and two collectables (so it has all the basics) in a future entry, and then I'll release the full project for those who want to use it as a base.


Thursday, October 19, 2023

Player and Rigidbody


 Hello there!

After many many many headaches, I found a way to work with the collision between Player and Puck.
Eventually I found this demo https://godotengine.org/asset-library/asset/1291 which uses
Rigidbody to control the player, and has box collisions. Besides, I've swapped the player's shape
with Godot's robot 3d animation from: https://godotengine.org/asset-library/asset/344 and
edited it to my needs. Now I have player movement, puck's collision and a mini rink to test.

Below you can see the current scene (tscn) and code for the player:


Edit: The robot character takes too much resources to load in the PS VITA and the game freezes after pressing the new game button. This means, for now, the robot is discarded and the generic capsule (MeshInstance) is used. [On the PC when I debug the project the resources problem doesn't happen, at all]
 

```
# Player
extends RigidBody


onready var raycast = $RayCast
onready var camera = $Target/Camera
onready var start_position = translation


func _physics_process(_delta):
    var dir = Vector3()
    dir.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
    dir.z = Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")

    # Get the camera's transform basis, but remove the X rotation such
    # that the Y axis is up and Z is horizontal.
    var cam_basis = camera.global_transform.basis
    var basis = cam_basis.rotated(cam_basis.x, -cam_basis.get_euler().x)


    apply_central_impulse(dir.normalized() /5)

    # Jumping code.
    if on_ground() and Input.is_action_pressed("jump"):
        apply_central_impulse(Vector3.UP)


# Test if there is a body below the player.
func on_ground():
    if raycast.is_colliding():
        return true
```


And now the player's camera (the one from the player scene, not for the main scene)

```
extends Camera


export var min_distance = 0.5
export var max_distance = 3.0
export var angle_v_adjust = 0.0
var collision_exception = []
var max_height = 2.0
var min_height = 0
onready var target_node: Spatial = get_parent()


func _ready():
    collision_exception.append(target_node.get_parent().get_rid())
    # Detaches the camera transform from the parent spatial node
    set_as_toplevel(true)


func _physics_process(_delta):
    var target_pos: Vector3 = target_node.global_transform.origin
    var camera_pos: Vector3 = global_transform.origin

    var delta_pos: Vector3 = camera_pos - target_pos

    # Regular delta follow

    # Check ranges
    if delta_pos.length() < min_distance:
        delta_pos = delta_pos.normalized() * min_distance
    elif delta_pos.length() > max_distance:
        delta_pos = delta_pos.normalized() * max_distance

    # Check upper and lower height
    delta_pos.y = clamp(delta_pos.y, min_height, max_height)
    camera_pos = target_pos + delta_pos

    look_at_from_position(camera_pos, target_pos, Vector3.UP)

    # Turn a little up or down
    var t = transform
    t.basis = Basis(t.basis[0], deg2rad(angle_v_adjust)) * t.basis
    transform = t
```


If you try that, your character will fall to the ground due to the physics, and you'll see a capsule rotating around the floor. To fix that, you have to go to the inspector (in your rigidbody) and lock the angular Axis. Example in the image below:






Monday, October 16, 2023

3D objects position - scoring

Now that we have a way to locate our objects (puck and net) in the screen grid space, it's time to make a function to check if someone has scored. To evaluate this condition, we need to know if the puck is in a certain range of the space:

(Note: I used the player object as a way to easily test the code.)
 
Is the puck between the net's lateral posts?
 
```
signal goal
func _process(delta):
    var netPos = netA.get_global_transform().origin.x
    var pPosition = get_global_transform().origin.x
    # Puck between the posts
    if pPosition > (netA_width/2) and pPosition < (netPos + netA_width):
        emit_signal("goal", pPosition)
       
onready var netA = .get_parent().get_node("GoalNet")
onready var netA_x = netA.get_node("StaticBody_postR").get_global_transform().origin.x
onready var netA_x2 = netA.get_node("StaticBody_postL").get_global_transform().origin.x
onready var netA_width = netA_x - netA_x2
```

Is the puck inside the net and not past the net?
 
```
signal goal
func _process(delta):
    var netPos = netA.get_global_transform().origin.z
    var pPosition = get_global_transform().origin.z
    # Puck inside the net
    if pPosition > (netPos - netA_depth) and (pPosition < netPos):
        emit_signal("goal", pPosition)

onready var netA = .get_parent().get_node("GoalNet")
onready var netA_z = netA.get_node("StaticBody_postZ").get_global_transform().origin.z
onready var netA_z2 = netA.get_node("StaticBody_netZ2").get_global_transform().origin.z
onready var netA_depth = netA_z - netA_z2
```
 

Is the puck between the rink's floor and the top post?

```
```

Finally, we merge all those evaluations into one script:

```
signal goal
func _process(delta):
    var netPos = netA.get_global_transform().origin
    var pPosition = get_global_transform().origin
    # Puck inside the net
    if pPosition.z > (netPos.z - netA_depth) and (pPosition.z < netPos.z) \
    and pPosition.x > (netA_width/2) and pPosition.x < (netPos.x + netA_width):
        emit_signal("goal", pPosition)

onready var netA = .get_parent().get_node("GoalNet")

onready var netA_x = netA.get_node("StaticBody_postR").get_global_transform().origin.x
onready var netA_x2 = netA.get_node("StaticBody_postL").get_global_transform().origin.x
onready var netA_width = netA_x - netA_x2

onready var netA_z = netA.get_node("StaticBody_postZ").get_global_transform().origin.z
onready var netA_z2 = netA.get_node("StaticBody_netZ2").get_global_transform().origin.z
onready var netA_depth = netA_z - netA_z2

```





Friday, October 13, 2023

Godot 3.5 AI

Since one of the "near" future steps for the ice puck game I had to see how to implement the NPC AI, and I want to leave a few documentation/guide videos. All with explanations and examples, plus a description of mine here so you can remember at a glance. [Remember you can use any of them if it fits you, I'm only giving hints]

State machine
https://youtu.be/RzUkBT7QwrU?si=OirslGHuHIs2kIDk
These are your usual platformer ones, mario like, where the enemy has a pattern that repeats over and over, no matter what.

Behaviour tree
https://youtu.be/YHUQY2Kea9U?si=oxl2viKZpWvxe0mG
These are more seen in fighting and shooter games, I believe racing games could go here as well. The enemy has a tree and different paths with minimum weights, to select the most efficient to their task. Your enemy doesn't take into account your life or score, it wants to finish you. 

GOAP
https://youtu.be/LhnlNKWh7oc?si=ct5vAyN4yCuv3nf_
The Goal Oriented Action Planning (GOAP),
which is the one I'm probably going to use. This one uses the priorities given for their goals, hence why for a sport game could be pretty useful. For example, the enemy takes into account if it has more or less goals than you, to decide if defence the net or attack.


Wednesday, October 11, 2023

3D objects position

 So... Another Godot for the PS Vita entry here.

There are a few ways to check if the puck enters the net, and for the NPC to follow the puck, I need to know its position. Therefor I had to see how to manage it in Godot 3.5, and that's what I want to write here for the people who want to do the same.

WARNING: This is the way I came up with. There could be more, and it may be different in other Godot versions, but for me, it works. Besides, this can be reused in any other project easily. Last but not least, the code and Idea below shows values in screen to give you an idea of how to use it, the numbers are by no means used in practice yet (I save that for another entry, maybe).

Without further ado, Code to see the positions of 3d objects in Godot 3.5

First, I took the "3D Squash the Creeps starter project" from https://github.com/gdquest-demos/godot-3-getting-started-2021/releases/tag/1.0.0 to have a test base (this way I don't have to create a new scene, and anyone can follow along without much problem). I cleaned a bit tho, no need of the mobs group or the scoring system for my test.






Then, I created and used signals to send the numbers from the Player and the creeper(Mob) to a UserInterface/Label node(ScoreLabel), so they can be printed in screen.  My added code is below:

```
# Creeper Code

signal creep
func _process(delta):
    var cPosition = get_global_transform()
    emit_signal("creep", str(cPosition))
```

```
# Player Code

signal player
func _process(delta):
    var pPosition = get_global_transform()
    emit_signal("player", str(pPosition))
```

Once I had the numbers, I just created two local variables on the label and printed them on screen.
```
# textPositions label code

extends Label

var player_position = 0
var creep_position = 0


func _on_Mob_creep(cPosition):
    creep_position =  cPosition

func _on_Player_player(pPosition):
    player_position =  pPosition

func _process(delta):
#    text = "PlayerPos: %s " % player_position
#    text = "CreeperPos: %s " % creep_position
    text = "PlayerPos: %s \n" %player_position + "CreeperPos: %s" %creep_position
```
 
Here you can see images of the result with the player in various positions




More on the position and the get_global_transform() here: https://ask.godotengine.org/40558/follow-node-movement-but-ignore-rotation

Furthermore, if you want the net's position from the label node, you can use:
(In my case, the label was the grand-grandchild of the player's sibling, so a few parents were needed)

```
onready var netA = get_parent().get_parent().get_parent().get_node("GoalNet").get_global_transform().origin
```

Saturday, October 7, 2023

New Engine

I had a secret halted demo game for a long time (due to some nasty bugs), so when I found out about the new Homebrew contest for the vita I knew it was time to squash them and release it: https://fuhen.homebrew-contest.com/submissions/23/ 
 
Besides, thanks to the FuHEN Homebrew Contest for PS VITA, I discoverd some options that I didn't know. And since this blog is to develope homebrew for that console, I decided to add the info here. After reading around the resources page: https://fuhen.homebrew-contest.com/resources/ I discover a new interesting option for coding my Ice Puck homebrew, which is the Godot Engine: https://github.com/SonicMastr/godot-vita/releases/tag/3.5-rc5-vita1 
 
Official documentation for Godot 3.5 here:

Then, I tried to make a test exporting the game "3D Squash the Creeps starter project" (which also is a good introduction to the engine for beginners). A video explanation of how to code it can be found here: https://www.youtube.com/watch?v=YiE9tcoCfhE and the code's official repo is here: https://github.com/gdquest-demos/godot-3-getting-started-2021/releases/tag/1.0.0 
 

 As explained in the README, for it to work on the vita, we need to make a few adjustments to the project settings. 
(For newbies who don't find that config in the engine) I will explain those steps below, with screenshots: 
 
 Go to Project > "Project Settings..." 
 

 
 
Click on Search and type "GLE2" 
Then click Quality and mark the box "Fallback to GLE2" 
 
 

 
Next it's turn to: Click on Search and type "ETC2" 
Then click on VRAM compression and mark the box "Import ETC2"
 
 
 
After that, close the window. 
 
 
Finally to export your game: 
 
Go to Project > "Export..." 

When it asks for an export template, choose and load the "vita_template_3.5.rc5.tpz" file from the github.

 



Time to make and play your own games!

Sunday, October 1, 2023

Some hockey flowcharts

 These are some basic flowcharts to have an idea of what each of the key game objects does or doesn't and how.

The room should load the board and the rink first, and that's our first diagram.

Score Board and Timer

After the board come the players/skaters and the puck

Players / Skaters








The Puck

 

Extra notes: Game wise, the most work will be into how the players behave and how the puck react to them and the rink limits [This means that, in the future, the flowcharts may be updated]. Besides, there will be more charts for extra objects positions and updates, e.g. the nets or the rink limits. In addition, later on, we'll also have menu and options.



Friday, May 19, 2023

Modularity

A well-designed program must be modular if it has a big size, and guess what, these projects are big. So, for this entry, we will be making a modular base for our program. This means, we want at least 3 modules (files):

  • main program

----------------------------------------------------------

Friday, April 21, 2023

Some documentation

This entry provides papers and documentation on many key topics like:

   - Ice Hockey - Quick Guide
   - Game Programming Patterns
   - Scaling Screen
   - Simple DirectMedia Layer (audio, keyboard, mouse, joystick, and graphics hardware)
   - NPCs movement, a.k.a. AI and Steering Behaviors.
   - Multiplayer: Client-Server Game Architecture
   - Game pad controls reference
   - Writing your own Load / Save routines
   - How to choose a License

* The list should be updated along the project.

---------------------------------------------------------------------------------------

- Ice Hockey - Quick Guide
https://www.tutorialspoint.com/ice_hockey/ice_hockey_quick_guide.htm

- Game Programming Patterns, Robert Nystrom
https://gameprogrammingpatterns.com/

- Scaling Screen
https://gamemaker.io/en/tutorials/the-basics-of-scaling-the-game-camera

- Simple DirectMedia Layer (audio, keyboard, mouse, joystick, and graphics hardware)
https://wiki.libsdl.org/SDL2/FrontPage

- Create a Hockey Game AI Using Steering Behaviors: Game Mechanics
https://gamedevelopment.tutsplus.com/tutorials/create-a-hockey-game-ai-using-steering-behaviors-game-mechanics--cms-23026

- The Nature of Code, Daniel Shiffman
https://natureofcode.com/book/

- Client-Server Game Architecture, Gabriel Gambetta
https://www.gabrielgambetta.com/client-server-game-architecture.html

- Game pad controls reference, from EA's NHL games
https://help.ea.com/ca/help/nhl/nhl-16/nhl-16-controls/

- Writing your own Load / Save routines, Myopic Rhino
https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/writing-your-own-load-save-routines-r1289/

Wednesday, March 1, 2023

Back to the Basics

 After more than two years, I've come back here, with the original idea still as hot as the first day. However, the approach of this entry is to create a new generic sample, so it can be checked in any system quickly.

Doing this, we can have something (similar to an SDL hello world) to test, without any Vita or SDK requirement. Besides, we can use it as a base for everything else.


First, we'll need to create our main.c file
(check https://lazyfoo.net/tutorials/SDL/01_hello_SDL/index2.php for more info)

----------------------------------------------------------