Skip to content

Commit 7c400f9

Browse files
LaroldsJubilantJunkyardavivaceZoomTen
authored
Add Part 3: coding a complete shoot-em-up game (#57)
Co-authored-by: Antonio Vivace <[email protected]> Co-authored-by: Zumi <[email protected]>
1 parent c085011 commit 7c400f9

File tree

88 files changed

+5659
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+5659
-4
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22
/target/
33
.DS_Store
44
/po/messages.pot
5+
/galactic-armada/src/generated
6+
/galactic-armada/dist/
7+
/galactic-armada/obj/

galactic-armada/Makefile

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# You can set the name of the .gb ROM file here
2+
PROJECTNAME = GalacticArmada
3+
SRCDIR = src
4+
LIBDIR = libs
5+
OBJDIR = obj
6+
DSTDIR = dist
7+
RESDIR = $(SRCDIR)/resources
8+
ASMDIR = $(SRCDIR)/main
9+
RESSPRITES = $(RESDIR)/sprites
10+
RESBACKGROUNDS = $(RESDIR)/backgrounds
11+
GENDIR = $(SRCDIR)/generated
12+
GENSPRITES = $(GENDIR)/sprites
13+
GENBACKGROUNDS = $(GENDIR)/backgrounds
14+
BINS = $(DSTDIR)/$(PROJECTNAME).gb
15+
16+
# Tools
17+
RGBDS ?=
18+
ASM := $(RGBDS)rgbasm
19+
GFX := $(RGBDS)rgbgfx
20+
LINK := $(RGBDS)rgblink
21+
FIX := $(RGBDS)rgbfix
22+
23+
# Tool flags
24+
ASMFLAGS := -L
25+
FIXFLAGS := -v -p 0xFF
26+
27+
# https://stackoverflow.com/a/18258352
28+
# Make does not offer a recursive wild card function, so here's one:
29+
rwildcard = $(foreach d,\
30+
$(wildcard $(1:=/*)), \
31+
$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d) \
32+
)
33+
34+
# https://stackoverflow.com/a/16151140
35+
# This makes it so every entry in a space-delimited list appears only once
36+
unique = $(if $1,\
37+
$(firstword $1) $(call unique,$(filter-out $(firstword $1),$1)) \
38+
)
39+
40+
# Collect ASM sources from ASMDIR and LIBDIR.
41+
ASMSOURCES_COLLECTED = \
42+
$(call rwildcard,$(ASMDIR),*.asm) $(call rwildcard,$(LIBDIR),*.asm)
43+
44+
OBJS = $(patsubst %.asm,$(OBJDIR)/%.o,$(notdir $(ASMSOURCES_COLLECTED)))
45+
46+
all: $(BINS)
47+
48+
# ANCHOR: generate-graphics
49+
NEEDED_GRAPHICS = \
50+
$(GENSPRITES)/player-ship.2bpp \
51+
$(GENSPRITES)/enemy-ship.2bpp \
52+
$(GENSPRITES)/bullet.2bpp \
53+
$(GENBACKGROUNDS)/text-font.2bpp \
54+
$(GENBACKGROUNDS)/star-field.tilemap \
55+
$(GENBACKGROUNDS)/title-screen.tilemap
56+
57+
# Generate sprites, ensuring the containing directories have been created.
58+
$(GENSPRITES)/%.2bpp: $(RESSPRITES)/%.png | $(GENSPRITES)
59+
$(GFX) -c "#FFFFFF,#cfcfcf,#686868,#000000;" --columns -o $@ $<
60+
61+
# Generate background tile set, ensuring the containing directories have been created.
62+
$(GENBACKGROUNDS)/%.2bpp: $(RESBACKGROUNDS)/%.png | $(GENBACKGROUNDS)
63+
$(GFX) -c "#FFFFFF,#cbcbcb,#414141,#000000;" -o $@ $<
64+
65+
# Generate background tile map *and* tile set, ensuring the containing directories
66+
# have been created.
67+
$(GENBACKGROUNDS)/%.tilemap: $(RESBACKGROUNDS)/%.png | $(GENBACKGROUNDS)
68+
$(GFX) -c "#FFFFFF,#cbcbcb,#414141,#000000;" \
69+
--tilemap $@ \
70+
--unique-tiles \
71+
-o $(GENBACKGROUNDS)/$*.2bpp \
72+
$<
73+
# ANCHOR_END: generate-graphics
74+
75+
compile.bat: Makefile
76+
@echo "REM Automatically generated from Makefile" > compile.bat
77+
@make -sn | sed y/\\/\\\\/\\\\\\\^/ | grep -v make >> compile.bat
78+
79+
80+
# ANCHOR: generate-objects
81+
# Extract directories from collected ASM sources and append "%.asm" to each one,
82+
# creating a wildcard-rule.
83+
ASMSOURCES_DIRS = $(patsubst %,%%.asm,\
84+
$(call unique,$(dir $(ASMSOURCES_COLLECTED))) \
85+
)
86+
87+
# This is a Makefile "macro".
88+
# It defines a %.o target from a corresponding %.asm, ensuring the
89+
# "prepare" step has ran and the graphics are already generated.
90+
define object-from-asm
91+
$(OBJDIR)/%.o: $1 | $(OBJDIR) $(NEEDED_GRAPHICS)
92+
$$(ASM) $$(ASMFLAGS) -o $$@ $$<
93+
endef
94+
95+
# Run the macro for each directory listed in ASMSOURCES_DIRS, thereby
96+
# creating the appropriate targets.
97+
$(foreach i, $(ASMSOURCES_DIRS), $(eval $(call object-from-asm,$i)))
98+
# ANCHOR_END: generate-objects
99+
100+
# Link and build the final ROM.
101+
$(BINS): $(OBJS) | $(DSTDIR)
102+
$(LINK) -o $@ $^
103+
$(FIX) $(FIXFLAGS) $@
104+
# Ensure directories for generated files exist.
105+
define ensure-directory
106+
$1:
107+
mkdir -p $$@
108+
endef
109+
110+
PREPARE_DIRECTORIES = \
111+
$(OBJDIR) $(GENSPRITES) $(GENBACKGROUNDS) $(DSTDIR)
112+
113+
$(foreach i, $(PREPARE_DIRECTORIES), $(eval $(call ensure-directory,$i)))
114+
115+
# Clean up generated directories.
116+
clean:
117+
rm -rfv $(PREPARE_DIRECTORIES)
118+
# Declare these targets as "not actually files".
119+
.PHONY: clean all
120+

galactic-armada/libs/input.asm

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2+
; It's straight from: https://gbdev.io/gb-asm-tutorial/part2/input.html
3+
; In their words (paraphrased): reading player input for gameboy is NOT a trivial task
4+
; So it's best to use some tested code
5+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6+
7+
include "src/main/utils/hardware.inc"
8+
9+
SECTION "Input", ROM0
10+
11+
Input::
12+
; Poll half the controller
13+
ld a, P1F_GET_BTN
14+
call .onenibble
15+
ld b, a ; B7-4 = 1; B3-0 = unpressed buttons
16+
17+
; Poll the other half
18+
ld a, P1F_GET_DPAD
19+
call .onenibble
20+
swap a ; A3-0 = unpressed directions; A7-4 = 1
21+
xor a, b ; A = pressed buttons + directions
22+
ld b, a ; B = pressed buttons + directions
23+
24+
; And release the controller
25+
ld a, P1F_GET_NONE
26+
ldh [rP1], a
27+
28+
; Combine with previous wCurKeys to make wNewKeys
29+
ld a, [wCurKeys]
30+
xor a, b ; A = keys that changed state
31+
and a, b ; A = keys that changed to pressed
32+
ld [wNewKeys], a
33+
ld a, b
34+
ld [wCurKeys], a
35+
ret
36+
37+
.onenibble
38+
ldh [rP1], a ; switch the key matrix
39+
call .knownret ; burn 10 cycles calling a known ret
40+
ldh a, [rP1] ; ignore value while waiting for the key matrix to settle
41+
ldh a, [rP1]
42+
ldh a, [rP1] ; this read counts
43+
or a, $F0 ; A7-4 = 1; A3-0 = unpressed keys
44+
.knownret
45+
ret

galactic-armada/libs/sporbs_lib.asm

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
; Sprite Objects Library - by Eievui
2+
;
3+
; This is a small, lightweight library meant to facilitate the rendering of
4+
; sprite objects, including Shadow OAM and OAM DMA, single-entry "simple" sprite
5+
; objects, and Q12.4 fixed-point position metasprite rendering.
6+
;
7+
; The library is only 127 bytes of ROM0, 160 bytes of WRAM0 for Shadow OAM, and a
8+
; single HRAM byte for tracking the current position in OAM.
9+
;
10+
; The library is relatively simple to use, with 4 steps to rendering:
11+
; 1. Call InitSprObjLib during initilizations - This copies the OAMDMA function to
12+
; HRAM.
13+
; 2. Call ResetShadowOAM at the beginning of each frame - This hides all sprites
14+
; and resets hOAMIndex, allowing you to render a new frame of sprites.
15+
; 3. Call rendering functions - Push simple sprites or metasprites to Shadow OAM.
16+
; 4. Wait for VBlank and call hOAMDMA - Copies wShadowOAM to the Game Boy's OAM in
17+
; just 160 M-cycles. Make sure to pass HIGH(wShadowOAM) in the a register.
18+
;
19+
; Copyright 2021, Eievui
20+
;
21+
; This software is provided 'as-is', without any express or implied
22+
; warranty. In no event will the authors be held liable for any damages
23+
; arising from the use of this software.
24+
;
25+
; Permission is granted to anyone to use this software for any purpose,
26+
; including commercial applications, and to alter it and redistribute it
27+
; freely, subject to the following restrictions:
28+
;
29+
; 1. The origin of this software must not be misrepresented; you must not
30+
; claim that you wrote the original software. If you use this software
31+
; in a product, an acknowledgment in the product documentation would be
32+
; appreciated but is not required.
33+
; 2. Altered source versions must be plainly marked as such, and must not be
34+
; misrepresented as being the original software.
35+
; 3. This notice may not be removed or altered from any source distribution.
36+
;
37+
38+
INCLUDE "src/main/utils/hardware.inc"
39+
40+
SECTION "OAM DMA Code", ROM0
41+
OAMDMACode::
42+
LOAD "OAM DMA", HRAM
43+
; Begin an OAM DMA, waiting 160 cycles for the DMA to finish.
44+
; This quickly copies Shadow OAM to the Game Boy's OAM, allowing the PPU to draw
45+
; the objects. hOAMDMA should be called once per frame near the end of your
46+
; VBlank interrupt. While an OAM DMA is running no sprites objects can be drawn
47+
; by the PPU, which makes it preferrable to run within the VBlank interrupt, but
48+
; it can be run at any point if more than 40 sprite objects are needed.
49+
; @param a: High byte of active Shadow OAM. Shadow OAM must be aligned to start
50+
; at the beginning of a page (low byte == $00).
51+
hOAMDMA::
52+
ldh [rDMA], a
53+
ld a, 40
54+
.wait
55+
dec a
56+
jr nz, .wait
57+
ret
58+
ENDL
59+
OAMDMACodeEnd::
60+
61+
SECTION "Initialize Sprite Object Library", ROM0
62+
63+
; A wrapper or the InitSprObjLib code
64+
; from: https://github.com/eievui5/gb-sprobj-lib
65+
; The library is relatively simple to get set up. First, put the following in your initialization code:
66+
; Initilize Sprite Object Library.
67+
InitSprObjLibWrapper::
68+
69+
call InitSprObjLib
70+
; Reset hardware OAM
71+
xor a, a
72+
ld b, 160
73+
ld hl, _OAMRAM
74+
75+
.resetOAM
76+
ld [hli], a
77+
dec b
78+
jr nz, .resetOAM
79+
80+
ret
81+
82+
; Initializes the sprite object library, copying things such as the hOAMDMA
83+
; function and reseting hOAMIndex
84+
; @clobbers: a, bc, hl
85+
InitSprObjLib::
86+
; Copy OAM DMA.
87+
ld b, OAMDMACodeEnd - OAMDMACode
88+
ld c, LOW(hOAMDMA)
89+
ld hl, OAMDMACode
90+
.memcpy
91+
ld a, [hli]
92+
ldh [c], a
93+
inc c
94+
dec b
95+
jr nz, .memcpy
96+
xor a, a
97+
ldh [hOAMIndex], a ; hOAMIndex must be reset before running ResetShadowOAM.
98+
ret
99+
100+
SECTION "Reset Shadow OAM", ROM0
101+
; Reset the Y positions of every sprite object that was used in the last frame,
102+
; effectily hiding them, and reset hOAMIndex. Run this function each frame
103+
; before rendering sprite objects.
104+
; @clobbers: a, c, hl
105+
ResetShadowOAM::
106+
xor a, a ; clear carry
107+
ldh a, [hOAMIndex]
108+
rra
109+
rra ; a / 4
110+
and a, a
111+
jr z, .skip
112+
ld c, a
113+
ld hl, wShadowOAM
114+
xor a, a
115+
.clearOAM
116+
ld [hli], a
117+
inc l
118+
inc l
119+
inc l
120+
dec c
121+
jr nz, .clearOAM
122+
ldh [hOAMIndex], a
123+
.skip
124+
ret
125+
126+
SECTION "Render Simple Sprite", ROM0
127+
; Render a single object, or sprite, to OAM.
128+
; @param b: Y position
129+
; @param c: X position
130+
; @param d: Tile ID
131+
; @param e: Tile Attribute
132+
; @clobbers: hl
133+
RenderSimpleSprite::
134+
ld h, HIGH(wShadowOAM)
135+
ldh a, [hOAMIndex]
136+
ld l, a
137+
ld a, b
138+
add a, 16
139+
ld [hli], a
140+
ld a, c
141+
add a, 8
142+
ld [hli], a
143+
ld a, d
144+
ld [hli], a
145+
ld a, e
146+
ld [hli], a
147+
ld a, l
148+
ldh [hOAMIndex], a
149+
ret
150+
151+
SECTION "Render Metasprite", ROM0
152+
; Render a metasprite to OAM.
153+
; @param bc: Q12.4 fixed-point Y position.
154+
; @param de: Q12.4 fixed-point X position.
155+
; @param hl: Pointer to current metasprite.
156+
RenderMetasprite::
157+
; Adjust Y and store in b.
158+
ld a, c
159+
rrc b
160+
rra
161+
rrc b
162+
rra
163+
rrc b
164+
rra
165+
rrc b
166+
rra
167+
ld b, a
168+
; Adjust X and store in c.
169+
ld a, e
170+
rrc d
171+
rra
172+
rrc d
173+
rra
174+
rrc d
175+
rra
176+
rrc d
177+
rra
178+
ld c, a
179+
; Load Shadow OAM pointer.
180+
ld d, HIGH(wShadowOAM)
181+
ldh a, [hOAMIndex]
182+
ld e, a
183+
; Now:
184+
; bc - Y, X
185+
; de - Shadow OAM
186+
; hl - Metasprite
187+
; Time to render!
188+
.loop
189+
; Load Y.
190+
ld a, [hli]
191+
add a, b
192+
ld [de], a
193+
inc e
194+
; Load X.
195+
ld a, [hli]
196+
add a, c
197+
ld [de], a
198+
inc e
199+
; Load Tile.
200+
ld a, [hli]
201+
ld [de], a
202+
inc e
203+
; Load Attribute.
204+
ld a, [hli]
205+
ld [de], a
206+
inc e
207+
; Check for null end byte.
208+
ld a, [hl]
209+
cp a, 128
210+
jr nz, .loop
211+
ld a, e
212+
ldh [hOAMIndex], a
213+
ret
214+
215+
SECTION "Shadow OAM", WRAM0, ALIGN[8]
216+
wShadowOAM::
217+
ds 160
218+
219+
SECTION "Shadow OAM Index", HRAM
220+
; The current low byte of shadow OAM.
221+
hOAMIndex::
222+
db

0 commit comments

Comments
 (0)