终于讲到伪3D效果了,这一块比较棘手,我从github上下载的老外写的例子一点点研究,研究了两三天才研究出原理,其实原理很TM简单,主要是那个老外写的代码太烂了,写了很多没用的东西,而且算法上还啰里啰嗦的不清楚,导致我阅读起来很费劲,我不知道有些代码到底干了些啥,事实上有些代码真的是相当于啥也没干,我真是无语了。而且注释还是西班牙语。。。
再就是定点小数的问题困扰了我半天的时间,虽然最后没搞懂,但是对于写代码还是没有很大的问题。
吐槽完了进入正题,这节课要做的效果如下图:
方向左右键可以控制地面的扭曲,看起来就像3D的一样,其实只是伪3D。卷轴滚动这个功能不止是可以做伪3D地面,还能做非常多的特效,玩过世嘉MD游戏的朋友不难发现很多游戏都有很华丽的扭曲特效或者伪3D特效,都是用这个功能做的,比如我知道的《蝙蝠侠与罗宾》这个游戏,可以说榨干了世嘉MD机能,各种华丽的伪3D特效,属实震撼。《魂斗罗-铁血兵团》里也有很华丽的伪3D特效。
素材图如下:
别的东西也没有了,基础操作就不说了,前面都讲的很详细,一路看过来的话,基础操作恐怕已经成为本能了。
少啰嗦,直接看代码:
#include <genesis.h>
#include <vdp.h>
#include "resources.h"//需要有多少行像素做伪3D的效果
#define LINE 224//输入
void HandleInput();//计算滚动数据
void CalculationScrollData();//把滚动数据变换成Int类型,为什么要变换呢?计算好滚动数据不能直接用吗?
//之所以需要转换数据,是因为世嘉MD里面计算浮点数的方式很特别,
//跟现在的浮点数计算方式完全不同,在世嘉MD里,写代码的时候还是以整数形式写,
//比如一个浮点数做++运算,看起来是+1,其实不是1,具体多少我也不知道,反正远远不到1,
//这种特性就满足了我们+0.01这种需求,看起来是+1,效果却是加零点几,
//我也是试验了很久才试验出来这个特性,深层原理我是没搞懂,学过计算机原理的大佬一看就懂了,
//不懂也没事,不必纠结这个地方,只需要记住这个特性以及用法就好了。
//之所以要用定点小数不直接用整数,是因为有时候直接用整数++会得到一个很大的值,
//而我们需要一个在一定时间内才能累积到1的一个数值,而不是每一帧+1
void ConvertScrollDataToInt();//浮点数(严格来说应该叫定点小数)的数组存放每一行像素的滚动数据
fix16 scrollData[LINE] = {0};//每一行像素的偏移值
fix16 offset = FIX16(0);//short类型的数组来存放scrollData数组转换成int后的结果
s16 con[LINE] = {0};int main()
{//世嘉MD支持两种分辨率,一种是320x224,另一种是256x224,//这里把分辨率改成256x224VDP_setScreenWidth256();//前面详细讲过,略u16 index = TILE_USERINDEX;//略VDP_setPalette(PAL0, yuyu.palette->data);//略VDP_drawImageEx(PLAN_A, &yuyu, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, index), 0, 0, FALSE, CPU);//略index += yuyu.tileset->numTile;//设置卷轴的滚动模式,前面也提到过VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE);while (TRUE){HandleInput();CalculationScrollData();ConvertScrollDataToInt();//就是这一个函数实现了伪3D的效果,其他的代码都是在处理我们需要的数据,然后把数据代入到这个函数中//这个函数意思是设置水平方向的以一行像素为单位的滚动参数//PLAN_A:卷轴A,上面的图片绘制到了卷轴A,所以这里就滚动卷轴A//136:从哪一行像素开始滚动,这个背景图的地面位置y坐标在136,也就是从上往下数,第136行像素,136之前的像素行都不会有滚动效果//con:滚动数据转换成int后的数组//LINE:滚动多少行,LINE是224,其实在这里不必滚动224行这么多,因为有136行是不滚动的,//背景图片高才192,192-136=56,LINE的值是56就足够了,如果把LINE改成55,那么地面最后一行像素就不会滚动VDP_setHorizontalScrollLine(PLAN_A, 136, con, LINE, CPU);VDP_waitVSync();}return 0;
}void HandleInput()
{u16 input = JOY_readJoypad(JOY_1);if (input & BUTTON_LEFT){//按下左键,偏移值递减//这里看起来是每帧递减1,其实不是的,这只是一个定点小数,效果相当于每帧递减零点几,很少的量offset--;}if (input & BUTTON_RIGHT){//按下右键,偏移值递增offset++;}if (input & BUTTON_A){//按下A键,偏移值复位为0offset = FIX16(0);}
}void CalculationScrollData()
{//这个函数就是伪3D地面的核心算法了,我们需要对每一行像素做偏移,每一行的偏移值都不一样,而且有规律,//第一行像素是不用偏移的,所以索引0直接是0就可以了,这个0用了FIX16()这个方法把0转成定点小数,不用转直接写0也是可以的//但是建议用FIX16()这个函数去转一下,如果括号里不是0,是别的数,那么结果将是千差万别scrollData[0] = FIX16(0);for (int i = 1; i < LINE; i++){//从第二行开始,每一行像素的偏移值 = 上一行像素的偏移值 + offset//fix16Add()是定点小数的加法scrollData[i] = fix16Add(scrollData[i - 1], offset);}
}void ConvertScrollDataToInt()
{for (int i = 0; i < LINE; i++){//这行没啥好解释的了,fix16转成int,一目了然con[i] = fix16ToInt(scrollData[i]);}
}
编译,得到rom.bin,用模拟器运行,伪3D地面出来了。