世嘉MD游戏开发【十二】:伪3D地面,Pseudo-3D

终于讲到伪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,
    //这里把分辨率改成256x224
    VDP_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键,偏移值复位为0
        offset = 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地面出来了。

发布了17 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq272508839/article/details/103448071