Gameboy游戏开发③-精灵高阶
上节课聊到了精灵的反向问题,GB因为卡带容量的限制不可能每个方向都单独制作,一般ACT类游戏只需要一个侧面,另一个方向是通过瓦块的镜像的来实现的。我们使用的gbdk类库提供了一个set_sprite_prop
方法,看文档该方法会接收一个8位的值,其中第5位为1时对应的精灵会做水平翻转。我们来看一下例子。
精灵的水平翻转
我们还是用上节课的例子来继续修改,我们的精灵当时做的是向右侧的。现在我们来修改一下当摇杆操作向左的时候进行镜像操作。
else if(joypad()==J_LEFT)
{
set_sprite_tile(0, (run_index+4)*2);
set_sprite_tile(1, (run_index+4)*2+2);
//向左走时,水平镜像精灵。因为我们的角色是两个精灵拼合出来的,所以需要分别进行镜像。
set_sprite_prop(0, S_FLIPX);
set_sprite_prop(1, S_FLIPX);
scroll_sprite(0, -2, 0);
scroll_sprite(1, -2, 0);
if(run_index==4)
{
run_index = 0;
}
else
{
run_index+=2;
}
}
运行看看效果,人物被镜像了。但…..恩对我们是原地镜像的。所以左右侧两个精灵没有调整位置。
GB游戏角色类
我们尝试封装一个游戏精灵类,默认是按16x16来支持,也就是同时支持两个瓦块。
首先我们新建一个 game_role.h 的头文件
#include <gb/gb.h>
#include <stdio.h>
/**
* 游戏内角色的结构体,因为GB的机能限制,最大只支持8x16像素的sprit,
* 因为我们每个角色使用32x32才能表示,所以一个数组来存储精灵的索引
*/
struct GameRole
{
//精灵两个下标的存放数组
UBYTE spritids[2];
//精灵运动起来时的动画针索引
UINT8 spritrun[3];
//精灵的运动状态
UINT8 spite_run_status;
//精灵的x坐标
UINT8 x;
//精灵的y坐标
UINT8 y;
//精灵的面部朝向
//1上,2右,3下,4左。我们这次只用左右方向即可
UINT8 direction;
};
//单个精灵瓦块的宽度
UINT8 sprite_size = 8;
/**
* 移动精灵方法,我们要在.c文件中实现的
*/
void movegamecharacter(struct GameRole* character, UINT8 x, UINT8 y);
我们再新建一个类文件 game_role.c
#include "game_role.h"
/**
* 移动精灵方法的实现
*/
void movegamecharacter(struct GameRole* character, UINT8 x, UINT8 y)
{
//随着精灵的运动修改精灵的下标
if(character->spite_run_status==2)
{
character->spite_run_status = 0;
}
else
{
character->spite_run_status++;
}
//运动状态时循环显示几个针的动画
set_sprite_tile(character->spritids[0], character->spritrun[character->spite_run_status]);
set_sprite_tile(character->spritids[1], character->spritrun[character->spite_run_status]+2);
if(x<character->x && character->direction==2) {
//向左移动
set_sprite_prop(character->spritids[0],S_FLIPX);
set_sprite_prop(character->spritids[1],S_FLIPX);
character->direction = 4;
}
if(x>character->x && character->direction == 4) {
//向右移动
set_sprite_prop(character->spritids[0],!(S_FLIPX));
set_sprite_prop(character->spritids[1],!(S_FLIPX));
character->direction = 2;
}
//根据移动方向,移动精灵位置
if(character->x!=x || character->y!=y) {
if(character->direction ==2){
move_sprite(character->spritids[0], x-4, y);
move_sprite(character->spritids[1], x + sprite_size-4, y);
}
else if (character->direction == 4)
{
move_sprite(character->spritids[0], x, y);
move_sprite(character->spritids[1], x - sprite_size, y);
}
}
}
接下来我们在主文件中引入此文件并重构下主文件,删除掉向左右移动时的代码。
#include <gb/gb.h>
#include <stdio.h>
#include "game_role.h"
#include "mario.c"
//定义角色
struct GameRole role;
/**
* 新增加的方法,初始化角色
*/
void initRole(UINT8 x, UINT8 y)
{
role.x = 0;
role.y = 0;
role.spritrun[0] = 8;
role.spritrun[1] = 12;
role.spritrun[2] = 16;
role.spite_run_status = 0;
set_sprite_tile(0, role.spritrun[role.spite_run_status]);
role.spritids[0] = 0;
set_sprite_tile(1, role.spritrun[role.spite_run_status]+2);
role.spritids[1] = 1;
role.direction = 2;
movegamecharacter(&role,x,y);
role.x = x;
role.y = y;
}
/**
* 新的mian方法
*/
void main()
{
SPRITES_8x16;
set_sprite_data(0, 20, mario);
initRole(28,112);
SHOW_SPRITES;
while (1)
{
if(joypad()==J_RIGHT)
{
movegamecharacter(&role,role.x+2,role.y);
role.x +=2;
}
else if(joypad()==J_LEFT)
{
movegamecharacter(&role,role.x-2,role.y);
role.x -= 2 ;
}
else
{
set_sprite_tile(0, 0);
set_sprite_tile(1, 2);
}
delay(80);
}
}
这会儿咱们的main方法重构完更简洁了。因为我们增加了一个类文件进来,在make前,我们还要修改下Makefile文件。
CC = /opt/gbdk/bin/lcc -Wa-l -Wl-m -Wl-j
BINS = main.gb
all: $(BINS)
%.o: %.c
$(CC) -c -o $@ $<
%.gb: main.o game_role.o
$(CC) -o $@ $^
clean:
rm -f *.o *.lst *.map *.gb *~ *.rel *.cdb *.ihx *.lnk *.sym *.asm
make
运行。
原计划本课还会讲到调色板,但因为咱们重构了一下角色类。内容有点多,怕吸收不好,调色板的内容我向后移动了。
如果觉得文章内容比较实用,期望获得更新通知,请关注公众号: