Fermer2
BrunniLe 25/10/2009 à 00:35
Ce soir j'ai voulu programmer un "petit" moteur de jeu de plate-forme tout bête pour Game Boy, un truc aussi con que genre:
u16 x, y, vx, vy;
extern u8 map[32][32];
void perso_init() {
    x = y = vx = vy = 0;
}
void perso_handle() {
    vy += 10;    // gravité
    x += vx, y += vy;
}
void perso_draw() {
    OAM[0] = y >> 8;
    OAM[1] = x >> 8;
}
void perso_check_col() {
    if (map[y >> 11][x >> 11])    // Collision bidon
        vy = 0;
}

Tout est clair dans ma tête donc, alors je me mets à l'écrire en ASM. Et là c'est le drâme, jugez par vous même (le moteur fait l'équivalent de ce code C):
	; Constantes
	.GRAVITY = 10
	.OAM = 0xfe00

	; Variables
	.area	_BSS
.x:				; position 24-bit fixed point (16.8)
	.ds 3
.y:
	.ds 3
.vx:				; vitesse 16-bit fixed point (8.8)
	.ds 2
.vy:
	.ds 2

	.area	_CODE
	; Initialisation du personnage
_perso_init::
	xor a			; a = 0
	ld hl, #.x
	ld (hl+), a		; x = 0
	ld (hl+), a
	ld (hl+), a
	ld (hl+), a		; y = 0
	ld (hl+), a
	ld (hl+), a
	ld (hl+), a		; vx = 0
	ld (hl+), a
	ld (hl+), a		; vy = 0
	ld (hl+), a
	ret

	; Gestion de la physique du personnage
_perso_handle::
	ld hl, #.vy		; vy += GRAVITY
	ld b, #.GRAVITY
	call add16_val8

	call load16		; push vy
	push bc
	ld hl, #.vx		; bc = vx
	call load16
	pop de			; de = vy
	call _perso_move	; move(vx, vy)
	
	ret

	; Bouge le personnage
	; bc = delta X, de = delta Y (1.7.8 signed fixed point)
_perso_move::
	push de

	ld d, #2		; Pour x et y
	ld hl, #.x

move_boucle:
	bit 7, b		; bc.signe
	jr nz, move_bc_negatif

	ld a, (hl)		; partie basse
	add c
	ld (hl+), a
	ld a, (hl)		; partie moyenne
	adc b
	ld (hl+), a
	ld a, (hl)		; partie haute
	adc #0
	ld (hl+), a
	jr move_suite

move_bc_negatif:
	ld a, b			;
	cpl			; }
	ld b, a			; rend bc positif
	ld a, c			; }
	cpl			;
	ld c, a			;
	
	ld a, (hl)		; partie basse
	sub c
	ld (hl+), a
	ld a, (hl)		; partie moyenne
	sbc b
	ld (hl+), a
	ld a, (hl)		; partie haute
	sbc #0
	ld (hl+), a

move_suite:
	dec d
	ret z			; terminé? (y traité)
	
	pop bc			; de a été poussé avant
	jr move_boucle

	; Retourne l'adresse de la map pour un point donné
	; BC = point voulu (x, y) par rapport au perso
	; shared_asm[0].w = adresse de la map
get_map_addr:
	ld hl, #(.x + 1)		; de = (x, y)
	ld d, (hl)
	ld hl, #(.y + 1)
	ld e, (hl)

	; (x, y) += (b, c)
	ld a, d
	add b
	ld d, a
	push af
	ld a, e
	add c
	ld e, a
	push af

	; (x, y) /= 8
	srl d
	srl d
	srl d
	srl e
	srl e
	srl e
	
	; Retenue pour (x, y) += (b, c) avant
	pop af
	ld a, e
	adc #0
	ld e, a
	pop af
	ld a, d
	adc #0
	ld d, a
	
	; On peut encore ajouter à (d, e) les 3 bits du haut de la position
	ld hl, #(.x + 2)		; d += (x.hi & 7) << 5
	ld a, (hl+)
	and #7
	swap a
	sla a
	add d
	ld d, a
	ld hl, #(.y + 2)		; pareil pour e
	ld a, (hl)
	and #7
	swap a
	sla a
	add e
	ld e, a
	
	; FIXME
	ld hl, #_bglvl01_map
	
	ld a, d				; bc = d % 32
	and #31
	ld c, a
	ld b, #0
	add hl, bc			; colonne correspondante dans la map

	ld b, e				; bc = e * 32 (largeur d'une map)
	srl b				; 3 bits partent sur c
	srl b
	srl b
	ld a, e				; 3 bits du bas -> en haut de c
	and #7
	swap a
	sla a
	ld c, a
	add hl, bc			; ligne correspondante dans la map
	ret

_perso_check_col::
	ld b, #0
	ld c, #0
	call get_map_addr		; check en haut à gauche
	ld a, (hl)
	and a
	ret z				; rien là?
	
	; Collision -> stoppe net
	ld hl, #.vy
	ld a, #0
	ld (hl+), a
	ld (hl+), a
	ret

	; Dessin du personnage dans le sprite 0
_perso_draw::
	ld hl, #.OAM
	ld bc, #(.y + 1)		; y
	ld a, (bc)
	add #16
	ld (hl+), a

	ld bc, #(.x + 1)		; x
	ld a, (bc)
	add #8
	ld (hl), a
	ret

	; Retourne la position du perso
	; HL = tableau de 6 octets
_perso_get_pos::
	; source = .x, dest = hl, count = 6
	push hl
	ld hl, #.x
	pop de
	ld bc, #6
	call memcpy
	ret

Je me dis que soit 1) les programmeurs de l'époque avaient sacrément du skill pour faire des jeux complets 2) je suis une buse totale mais je pense qu'il y a beaucoup des deux happy