Initial commit

This commit is contained in:
thecookingsenpai 2023-12-25 13:24:14 +01:00
commit aa03bf3a21
5 changed files with 335 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
README.pdf
.luacheckrc

BIN
3DSenpai.3dsx Normal file

Binary file not shown.

104
README.md Normal file
View File

@ -0,0 +1,104 @@
# 3DSenpai
This documentation describes how the 3DSenpai engine works.
3DSenpai is a 2D (despite the name) engine that manages the logic and the GUI of Nintendo 3DS
homebrews that use [Lua Player Plus 3DS engine](https://github.com/Rinnegatamante/lpp-3ds) by [Rinnegatamante](https://rinnegatamante.it/).
Despite being tought for creating games, it is totally possible to use the engine to create
graphical applications (e.g. managing navigation, menus, etc).
## Attached Example
The attached example (index.lua) contains a demo application that shows how to use the engine.
Feel free to use it as a reference and a starting point for your applications.
## Global objects
To better manage the workflow of the game, some global objects are created at runtime and
are accessed during the game loop.
### Program
This object contains informations about the game such as author, version, title and so on.
### emptyElement
This object is a prototype of an empty element that is used to initialize an element and standardize its properties.
### Scene
This object contains the character object, the currentScene property, the imagesToDraw table
and the environment object.
#### Scene.environment
This is the most important object of the scene. It represents every pixel as a tile and is used to define, determine and manipulate the position and behavior of the elements in the scene.
It contains a size property with the width and height of the screen and the matrix table which is initialized by the setScene function as a 2D array representing every tile content. It is
accessed through:
Scene.environment.matrix[x][y]
## initalLoad
This method is called just after all the variables have been initialized.
Normally, you would use it to set the first scene through setScene method and load
everything you need to start the game.
### setScene
This method accepts a scene method that is called to obtain the desired scene properties.
Additionally, it resets the Scene.environment.matrix property to the default values.
These properties are then assigned to the Scene.currentScene and Scene.imagesToDraw objects.
### Scene methods
These methods are to be created by the developer.
For setScene to be effective, the scene method should return a table containing two booleans
that define whether or not the background and the main character should be drawn, and a table containing every element that should be drawn.
## The main loop
The game engine revolves around a mainLoop function that keep calling
keyEvents and mainGUI functions.
## keyEvents
This function keep the inputs monitored for key events and react consequently.
## mainGUI
This function loads the Scene object and, if specified, draws both the background and
the main character.
Then, if any, it parses the Scene.imagesToDraw table and draws the elements images that
are specified in it.
## Elements methods
These methods are a set of functions that help the developer create, manage and inspect elements in the current scene.
### isElement
This function returns wheter or not an element covers the specified coordinates (x and y).
A boolean is returned.
### elementPosition
If an element is in the elementsTable table, it returns the element object. Else, it returns nil.
### placeElement
This function is used to place an element in the scene.
It checks whether the element is already placed and if the tiles that should be covered are free
or not.
It returns a boolean and a string containing the result of the operation.
### removeElement
This function removes an element from the scene by checking its existence and position through the elementsTable and the the scene environment.Matrix.
### checkTileCollision
Given a set of coordinates (x and y), this function checks whether there is an element that covers the specified coordinates and if the element has the Collision property set to true.

BIN
assets/character.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

227
index.lua Normal file
View File

@ -0,0 +1,227 @@
Screen.disable3D() --Disabling 3D for the top screen
-- SECTION Program object
local Program = {
title = "3DSenpai",
dir = "0_LPP_Test"
}
-- !SECTION Program object
-- SECTION Misc objects
local emptyElement = {
Element = nil, -- name of the element
Type = 0, -- 0 = empty, 1 = element, 2 = creature, 3 = object
Collision = false, -- true / false
Size = { 0, 0 }, -- Size of the element
StartingPosition = { 0, 0 }, -- Starting position of the element
Image = nil, -- Image of the element (object)
Screen = TOP_SCREEN, -- Screen of the element
Properties = {} -- Properties of the element (e.g. health, damage, etc.)
}
local elementsTable = {} -- {name = {x,y}}
-- !SECTION Misc objects
-- SECTION Elements object
local Scene = {
environment = {
size = { x = 400, y = 240 },
matrix = {}
},
character = {
image = "assets/character.png",
imageLoaded = nil,
position = { x = 176, y = 96 }
},
currentScene = nil, -- Current scene method
imagesToDraw = {} -- A table containing the images to draw each frame
}
-- !SECTION Elements object
-- SECTION Scenes
-- Scenes are functions that are called to draw the scene on the screen at the
-- beginning of the program or when the user changes the scene (e.g. next level)
local firstLevel = function ()
local isBackground = true
local isCharacter = true
local imagesToDraw = {}
return {isBackground, isCharacter}, imagesToDraw
end
-- !SECTION Scenes
local setScene = function (scene) -- Sets the current scene method
-- Reset the environment matrix
for i = 1, Scene.environment.size.x do
Scene.environment.matrix[i] = {}
-- Each pixel of the environment contains the informations about the element on it (if any)
for j = 1, Scene.environment.size.y do
Scene.environment.matrix[i][j] = {emptyElement}
end
end
local settings = scene() -- Call the scene method that returns settings and images to draw
Scene.currentScene = settings[0]
Scene.imagesToDraw = settings[1]
return true
end
local initialLoad = function ()
System.currentDirectory("/3ds/" .. Program.dir .. "/")
Scene.character.imageLoaded = Screen.loadImage(System.currentDirectory()
.. Scene.character.image)
-- Setting the first scene
setScene(firstLevel)
end
-- SECTION Controls check
local keyEvents = function ()
local pad = Controls.read() -- Read Controls
if (Controls.check(pad, KEY_START)) then -- check if start is pressed
System.exit() -- Exit back to HBL
-- Movements with bounds
elseif (Controls.check(pad, KEY_DUP)) then
Scene.character.position.y = Scene.character.position.y - 1
if (Scene.character.position.y > 195) then
Scene.character.position.y = 195
end
elseif (Controls.check(pad, KEY_DDOWN)) then
Scene.character.position.y = Scene.character.position.y + 1
if (Scene.character.position.y < 5) then
Scene.character.position.y = 5
end
elseif (Controls.check(pad, KEY_DRIGHT)) then
Scene.character.position.x = Scene.character.position.x + 1
if (Scene.character.position.x > 355) then
Scene.character.position.x = 355
end
elseif (Controls.check(pad, KEY_DLEFT)) then
Scene.character.position.x = Scene.character.position.x - 1
if (Scene.character.position.x < 5) then
Scene.character.position.x = 5
end
end
end
-- !SECTION Controls check
local mainGUI = function ()
-- Load the current scene
local isBackground = Scene.currentScene[1]
local isCharacter = Scene.currentScene[2]
-- Draw the background
if (isBackground) then
Screen.fillRect(5, 395, 5, 235, Color.new(255, 0, 0), TOP_SCREEN)
end
-- Draw the character
if (isCharacter) then
Screen.drawImage(Scene.character.position.x, Scene.character.position.y,
Scene.character.imageLoaded, TOP_SCREEN)
end
-- Draw the images listed in the imagesToDraw table
for i, image in pairs(Scene.imagesToDraw) do
-- REVIEW Should the element be placed in the scene here with 'i'?
Screen.drawImage(image.StartingPosition[0], image.StartingPosition[1],
image.Image, image.Screen)
end
end
-- SECTION Methods
local isElement = function (x, y)
if (Scene.environment.matrix[x][y].Element == nil) then
return false
else
return true
end
end
local elementPosition = function (elementName)
if (elementsTable[elementName] == nil) then
return nil
end
return elementsTable[elementName]
end
local placeElement = function (x, y, element, imagePath)
local screen = element.screen or TOP_SCREEN
-- Ensure the element is not already placed
if (elementPosition(element.name) ~= nil) then
return false, "alreadyplaced" -- Element already placed
end
-- Ensure the tiles are empty
for i = x + 1, x + element.width do
for j = y + 1, y + element.height do
if isElement(x, y) then
return false, "notempty" -- Tile is not empty
end
end
end
-- Loading the image
local imageObject = Screen.loadImage(System.currentDirectory() .. imagePath)
-- Setting the starting cell
Scene.environment.matrix[x][y].Element = element.name
Scene.environment.matrix[x][y].Type = element.type
Scene.environment.matrix[x][y].Collision = element.collision
Scene.environment.matrix[x][y].StartingPosition = {x, y}
Scene.environment.matrix[x][y].Size = {element.width, element.height}
Scene.environment.matrix[x][y].Image = imageObject
Scene.environment.matrix[x][y].Screen = screen
Scene.environment.matrix[x][y].Properties = element.properties
-- Filling the matrix with the element
for i = x + 1, x + element.width do
for j = y + 1, y + element.height do
Scene.environment.matrix[i][j] = Scene.environment.matrix[x][y]
end
end
-- Adding the element to the elements table
elementsTable[element.name] = {x, y}
return true, "placed"
end
local removeElement = function (elementName)
-- Getting the element coordinates
local coordinates = elementPosition(elementName)
-- Check if the element is in the elements table
if (coordinates == nil) then
return
end
local x = coordinates[1]
local y = coordinates[2]
-- Setting the starting cell
Scene.environment.matrix[x][y] = {emptyElement}
-- Filling the matrix with the element
for i = x + 1, x + element.width do
for j = y + 1, y + element.height do
Scene.environment.matrix[i][j] = Scene.environment.matrix[x][y]
end
end
end
local checkTileCollision = function (x, y)
if (Scene.environment.matrix[x][y].Collision) then
return true
else
return false
end
end
-- !SECTION Methods
local mainLoop = function ()
Screen.waitVblankStart() -- Screen related stuff
Screen.refresh() -- Other Screen related stuff
Screen.clear(TOP_SCREEN) -- Clear top screen
Screen.clear(BOTTOM_SCREEN) -- Clear bottom screen
-- Keypress handling
keyEvents() -- Check for key events
-- Elements on screen
mainGUI()
Screen.flip() -- More screen related stuff
end
-- START MAIN LOGIC
initialLoad()
-- Continuously loop
while (true) do
mainLoop() -- Draw to screen
end