Bài 9: di chuyển sprite



Trong bài 6, ta đã viết chương trình hiển thị sprite lên màn hình. Sprite là phần hình ảnh phía trước phông nền, có thể di chuyển, chẳng hạn như nhân vật, vật thể trong game. Trong bài này sẽ đề cập đến việc làm thế nào để di chuyển sprite bằng tay cầm.
Trước hết, ở đây xem lại 3 mệnh lệnh so sánh. Đối tượng so sánh có thể là giá trị trực tiếp hoặc giá trị tại một địa chỉ.

CMP ; so sánh với A
CPX ; so sánh với X
CPY ; so sánh với Y

Chẳng hạn như

CMP #$10 ; so sánh A với số thập lục $10
CPX #16 ; so sánh X với số thập phân 16
CPY $2002 ; so sánh Y với giá trị tại địa chỉ $2002

Khi tiến hành so sánh thì các flag N (Negative), Z (Zero) và C (carry) của Status Register sẽ biến đổi, tùy thuộc vào kết quả. Sau khi so sánh, nó sẽ nhảy đến label cần thiết bằng lệnh phân nhánh có điều kiện. Nếu kếu quả so sánh trước đó là 0 thì Zero flag được lập nên có thể lược bỏ CMP #0 khi sử dụng chung với BEQ và BNE.

BEQ ifEqual ; nếu kết quả bằng thì nhảy tới label ifEqual
BNE ifNotEqual ; nếu kết quả không bằng thì nhảy tới label ifNotEqual


Chương trình di chuyển sprite

Trong các bài trước, ta đã viết được chương trình hiển thị code và điều khiển tay cầm. Kết hợp với bài này để viết chương trình di chuyển sprite thông qua các nút lên, xuống, trái, phải của tay cầm.
Tải file này, giải nén và được kage9.asm, kage.spr, kage.bkg và kage.pal. Đặt chung tất cả vào thư mục NESASM, thực hiện file lệnh và được KAGE9.NES.

; Chương trình di chuyển sprite

; INES header
.inesprg 1 ; - Chọn bank 1 trong program
.ineschr 1 ; - chọn bank 1 trong graphic
.inesmir 0 ; - đối xứng gương theo đường ngang
.inesmap 0 ; - Mapper số 0

.bank 1 ; bank 1
.org $FFFA ; bắt đầu từ $FFFA

.dw 0 ; ngắt VBlank
.dw Start ; ngắt reset. Khi khởi động và khi Reset nhảy tới Start
.dw 0 ; phát sinh do ngắt phần cứng và ngắt phần mềm

.bank 0 ; bank 0
.org $0000 ; bắt đầu từ $0000
X_Pos .db 0 ; biến số tọa độ X của sprite ($0000) 
Y_Pos .db 0 ; biến số tọa độ Y của sprite ($0001)

.org $8000 ; bắt đầu từ $8000 
Start:
lda $2002 ; khi phát sinh VBlank thì bit 7 của $2002 là 1
bpl Start ; trong khi bit 7 là 0 thì nhảy đến vị trí label Start, đợi vòng lặp

; Định dạng Register điều khiển PPU
lda #001000 
sta $2000
lda #000110 ; tắt sprite và BG khi đang định dạng
sta $2001

ldx #$00 ; cho X về 0

; chỉ định địa chỉ load palette $3F00 vào Register địa chỉ $2006 trong VRAM
lda #$3F 
sta $2006 
lda #$00 
sta $2006

loadPal: 
lda tilepal, x

sta $2007 ; đọc giá trị palette vào $2007

inx ; tăng giá trị X lên 1

cpx #32 ; so sánh X với 32
bne loadPal ; nếu kết quả so sánh trên không bằng thì nhảy tới loadpal

; định dạng tọa độ sprite
lda X_Pos_Init
sta X_Pos
lda Y_Pos_Init
sta Y_Pos

; Định dạng Register 2 điều khiển PPU
lda #011110 ; bật sprite và BG 
sta $2001

mainLoop: ; vòng lặp chính
lda $2002 ; khi phát sinh VBlan thì bit 7 của $2002 là 1
bpl mainLoop ; trong khi bit7 là 0 thì nhảy tới mainLoop, đợi lặp

; vẽ sprite
lda #$00 
sta $2003 ; chứa địa chỉ RAM của sprite 

lda Y_Pos ; load tọa độ Y
sta $2004 ; chứa tọa độ Y vào Register

lda #00 
sta $2004 ; chứa 0, chỉ định sprite 0
sta $2004 ; không xoay ngược sprite

lda X_Pos ; load giá trị tọa độ X
sta $2004 ; chứa tọa độ X vào Register

; chuẩn bị I/O Register
lda #$01
sta $4016
lda #$00 
sta $4016

; kiểm tra nút có bị nhấn không 
lda $4016 ; bỏ qua nút A
lda $4016 ; bỏ qua nút B
lda $4016 ; bỏ qua Select
lda $4016 ; bỏ qua Start
lda $4016 ; nút lên
and #1 ; AND #1
bne UPKEYdown ; nếu khác 0 tức nút bị nhấn, nhảy tới UPKeydown

lda $4016 ; nút xuống
and #1 ; AND #1
bne DOWNKEYdown ; nếu khác 0 tức nút bị nhấn, nhảy tới DOWNKeydown

lda $4016 ; nút trái
and #1 ; AND #1
bne LEFTKEYdown ; nếu khác 0 tức nút bị nhấn, nhảy tới LEFTKeydown

lda $4016 ; nút phải
and #1 ; AND #1
bne RIGHTKEYdown ; nếu khác 0 tức nút bị nhấn, nhảy tới RIGHTKeydown
jmp NOTHINGdown ; nếu không có nút nào bị nhấn thì nhảy tới NOTHINGdown

UPKEYdown:
dec Y_Pos ; giảm 1 tọa độ Y. Vì là Zero page nên có thể rút ngắn mệnh lệnh
; lda Y_Pos ; load tọa độ Y
; sbc #1 ; giảm #1
; sta Y_Pos ; chứa tọa độ Y

jmp NOTHINGdown

DOWNKEYdown:
inc Y_Pos ; tăng 1 trong giá trị tọa độ Y
jmp NOTHINGdown

LEFTKEYdown:
dec X_Pos ; giảm 1 tọa độ X
jmp NOTHINGdown 

RIGHTKEYdown:
inc X_Pos ; tăng 1 tọa độ X
; sau đây là NOTHINGdown nên không cần Jump

NOTHINGdown:
jmp mainLoop ; trở lại ban đầu mainLoop

; dữ liệu ban đầu 
X_Pos_Init .db 20 ; giá trị ban đầu của tọa độ X
Y_Pos_Init .db 40 ; giá trị ban đầu của tọa độ Y

tilepal: .incbin "kage.pal" ; include palette

.bank 2 ; bank 2
.org $0000 ; bắt đầu từ $0000

.incbin "kage.bkg" ; include BG
.incbin "kage.spr" ; include spr


Dùng giả lập FCEU để chạy KAGE9.NES vừa tạo ra, có thể vào Menu Debug-->Hex editor để quan sát tọa độ X, Y biến đổi khi di chuyển sprite tại $0000 (tọa độ X) và $0001 (tọa độ Y) trong Memory.

0 bình luận :

Post a Comment

 
Top