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