GameBoy Advance的,当玩家移动的物体不会显示在正确的位置(GameBoy Advanc

2019-10-20 03:15发布

这可能需要一段时间来解释 - 去品尝小吃你读到这一段时间。

我开发++中的音乐游戏机在C(我是一个相当新的程序员)一个2D益智游戏构建平台。 直到昨天晚上,我一直在做,我用这是GBA的屏幕的尺寸水平测试phyics引擎(只是一些轴线对齐边框的东西)。 然而,最后一场比赛将要求有一个水平,这个水平比屏幕的尺寸大,所以我试图建立一个系统,它允许GBA的屏幕跟随玩家,并作为一个结果,我只好动用一切在相对于屏幕的偏移屏幕。

但是,我有麻烦时,我显示可拿起和操纵的水平立方体。 每当玩家移动,屏幕上的方块的位置似乎漂离的水平它们的实际位置了。 这就像在那里绘制立方体为单个帧同步的 - 如果我暂停当玩家移动时,框显示在完全正确的位置,本场比赛,但是当我取消暂停,他们飘出来的地方,直到玩家停止再次移动。

我的类的简要描述 - 有一个基类称为对象定义(X,Y)位置和宽度和高度,有从对象继承,并增加了速度分量的实体类,并且从实体继承的字符类并增加了运动功能。 我的球员是一个Character对象,而我要拿起立方体是实体对象的数组。 无论是球员和立方体阵列是二级类,这也从Object继承的成员。

我怀疑问题出在最后的代码示例中,然而,对于什么,我试图做我在一个稍微更符合逻辑顺序排列的样品完全理解。

这里有水平的截头:

class Level : public Object
{
    private:
        //Data
        int backgroundoffsetx;
        int backgroundoffsety;

        //Methods
        void ApplyEntityOffsets();
        void DetermineBackgroundOffsets();

    public:
        //Data
        enum {MAXCUBES = 20};

        Entity cube[MAXCUBES];
        Character player;
        int numofcubes;

        //Methods
        Level();
        void Draw();
        void DrawBackground(dimension);
        void UpdateLevelObjects();
};

...和实体:

class Entity : public Object
{   
    private:
        //Methods
        int GetScreenAxis(int &, int &, const int, int &, const int);

    public: 
        //Data
        int drawx;  //Where the Entity's x position is relative to the screen
        int drawy;  //Where the Entity's y position is relative to the screen

        //Methods
        void SetScreenPosition(int &, int &);
};

这里是我的主游戏循环中的相关部分:

//Main loop
while (true)
{
    ...

    level.MoveObjects(buttons);
    level.Draw();
    level.UpdateLevelObjects();

    ...
}

由于精灵都显示在暂停时正确的地方的路上,我敢肯定,问题不在于MoveObjects()这决定了相对于水平等级玩家和立方体的poitions。 使叶片Draw()UpdateLevelObjects()

好吧, Draw() 我在事件提供这一点,这不是我的立方体正在显示不正确,但水平和平台时,他们坐(我不认为这是问题,但可能)。 Draw()只要求一个相关功能, DrawBackground()

/**
Draws the background of the level;
*/
void Level::DrawBackground(dimension curdimension)
{
    ...

    //Platforms
    for (int i = 0; i < numofplatforms; i++)
    {
        for (int y = platform[i].Gety() / 8 ; y < platform[i].GetBottom() / 8; y++)
        {
            for (int x = platform[i].Getx() / 8; x < platform[i].GetRight() / 8; x++)
            {
                if (x < 32)
                {
                    if (y < 32)
                    {
                        SetTile(25, x, y, 103);
                    }
                    else
                    {
                        SetTile(27, x, y - 32, 103);
                    }
                }
                else
                {
                    if (y < 32)
                    {
                        SetTile(26, x - 32, y, 103);
                    }
                    else
                    {
                        SetTile(28, x - 32, y - 32, 103);
                    }
                }
            }
        }
    }
}

这不可避免地需要解释的一些量。 我的平台,以像素为单位,但在8×8像素的瓷砖显示,所以我必须划分它们的大小这个循环。 SetTile()首先需要screenblock数。 我使用的显示平台的背景层为64×64瓦,所以需要每个32×32×2瓦到screenblocks显示它们。 该screenblocks编号为25-28。 103是我tilemap的瓦数。

这里的UpdateLevelObjects()

/**
Updates all gba objects in Level
*/
void Level::UpdateLevelObjects()
{
    DetermineBackgroundOffsets();
    ApplyEntityOffsets();

    REG_BG2HOFS = backgroundoffsetx;
    REG_BG3HOFS = backgroundoffsetx / 2;    
    REG_BG2VOFS = backgroundoffsety;
    REG_BG3VOFS = backgroundoffsety / 2;

    ...

    //Code which sets player position (drawx, drawy);

    //Draw cubes
    for (int i = 0; i < numofcubes; i++)
    {
        //Code which sets cube[i] position to (drawx, drawy);
    }
}

REG_BG位是其允许背景层要由像素的数目垂直和水平偏移的GBA的寄存器。 这些偏移在第一计算DetermineBackgroundOffsets()

/**
Calculate the offsets of screen based on where the player is in the level
*/
void Level::DetermineBackgroundOffsets()
{
    if (player.Getx() < SCREEN_WIDTH / 2)   //If player is less than half the width of the screen away from the left wall of the level
    {
        backgroundoffsetx = 0;
    }
    else if (player.Getx() > width - (SCREEN_WIDTH / 2))    //If player is less than half the width of the screen away from the right wall of the level
    {
        backgroundoffsetx = width - SCREEN_WIDTH;   
    }
    else    //If the player is in the middle of the level
    {
        backgroundoffsetx = -((SCREEN_WIDTH / 2) - player.Getx());
    }

    if (player.Gety() < SCREEN_HEIGHT / 2)
    {
        backgroundoffsety = 0;
    }
    else if (player.Gety() > height - (SCREEN_HEIGHT / 2))
    {
        backgroundoffsety = height - SCREEN_HEIGHT; 
    }
    else
    {
        backgroundoffsety = -((SCREEN_HEIGHT / 2) - player.Gety());
    }
}

只是要清楚, width是指以像素级的宽度,而SCREEN_WIDTH指的是GBA的屏幕宽度的恒定值。 此外,比较遗憾的是懒惰的重复。

这里的ApplyEntityOffsets

/**
Determines the offsets that keep the player in the middle of the screen
*/
void Level::ApplyEntityOffsets()
{
    //Player offsets
    player.drawx = player.Getx() - backgroundoffsetx;
    player.drawy = player.Gety() - backgroundoffsety;

    //Cube offsets
    for (int i = 0; i < numofcubes; i++)
    {
        cube[i].SetScreenPosition(backgroundoffsetx, backgroundoffsety);
    }
}

基本上这个中心屏幕上的播放器时,它在水平的中间,并允许它移动到边缘时,对水平的边缘屏幕颠簸。 至于立方体:

/**
Determines the x and y positions of an entity relative to the screen
*/
void Entity::SetScreenPosition(int &backgroundoffsetx, int &backgroundoffsety)
{
    drawx = GetScreenAxis(x, width, 512, backgroundoffsetx, SCREEN_WIDTH);
    drawy = GetScreenAxis(y, height, 256, backgroundoffsety, SCREEN_HEIGHT);
}

忍耐一下 - 我将解释在某一时刻的512和256。 这里的GetScreenAxis()

/**
Sets the position along an axis of an entity relative to the screen's position
*/
int Entity::GetScreenAxis(int &axis, int &dimensioninaxis, const int OBJECT_OFFSET, 
                            int &backgroundoffsetaxis, const int SCREEN_DIMENSION)
{
    int newposition;
    bool onawkwardedgeofscreen = false;

    //If position of entity is partially off screen in -ve direction
    if (axis - backgroundoffsetaxis < dimensioninaxis)
    {
        newposition = axis - backgroundoffsetaxis + OBJECT_OFFSET;
        onawkwardedgeofscreen = true;
    }
    else
    {
        newposition = axis - backgroundoffsetaxis;
    }

    if ((newposition > SCREEN_DIMENSION) && !onawkwardedgeofscreen)
    {
        newposition = SCREEN_DIMENSION;     //Gets rid of glitchy squares appearing on screen
    }

    return newposition;
}

OBJECT_OFFSET (512和256)是GBA具体的东西-一个对象的x或y位置设置为负数不会做你通常打算什么-它弄乱了用于显示它的精灵。 但还有一招:如果你想设置一个负的X位置,您可以添加512至负数,并且(例如,如果你打算把它设置为-1,然后将其设置为精灵将出现在正确的地方512 + -1 = 511)。 同样,加入256个工程负Y位置(这是所有相对于屏幕,而不是水平)。 最后的if语句挡,如果他们通常会更远显示在屏幕显示的分数的立方体,为试图显示他们太远的出现,再次GBA具体的东西出问题平方结果。

你是一个绝对的圣人,如果你已经走到这一步已经阅读一切。 如果你能找到什么潜在的可能导致该漂流立方体,我将非常感激。 此外,为了普遍提高我的代码任何提示将不胜感激。


编辑:GBA的对象是设置播放器和多维数据集位置更新的方法如下:

for (int i = 0; i < numofcubes; i++)
{
    SetObject(cube[i].GetObjNum(),
      ATTR0_SHAPE(0) | ATTR0_8BPP | ATTR0_REG | ATTR0_Y(cube[i].drawy),
      ATTR1_SIZE(0) | ATTR1_X(cube[i].drawx),
      ATTR2_ID8(0) | ATTR2_PRIO(2));
}

Answer 1:

我会解释这个答案头号如何位运算工作,如何让说,以0至255(256种组合)的可能值的字节包含所有GBA控制印刷机。 这类似于你的X / Y位置问题。

控件

Up - Down - Left - Right - A - B - Select - Start

这些都是GameBoy的色彩控制,我认为GameBoy的高级有更多的控制。 因此,一共有8所控制。 每个控制既可以压制或(按住)未按。 这将意味着在每个控制应该只使用一个数字10 。 由于10仅需1的信息比特。 在一个字节可以存储多达8个不同的位,这适用于所有的控件。

现在,你可能会想我怎么能结合在一起他们加入的东西? 是的,你可以这样做,但它使得它非常复杂,难以理解和它给你这个问题。

假设你有水的那一半是空的玻璃和你添加更多的水进去,你想新添加的水从老水分开..你就不能这样做,因为水都成了一个水没办法撤销此(除非我们标记每个水moleclue,我们是不是外星人尚未..大声笑)。

但随着位运算,它使用数学弄清楚这一点恰恰是一个10位的全码流(名单)英寸

所以,你首先要做的就是你给每一位来控制。 每个位的2个二进制的倍数,所以你只要保持价值倍增。

Up - Down - Left - Right - A - B - Select - Start
1 - 2 - 4 - 8 - 16 - 32 - 64 - 128

此外位运算不仅用于找出哪一位是10 ,你也可以用它们来某些事情结合在一起。 控制做好这一点,因为你可以按下并举行一次多个按钮。

下面是我用它来找出哪些是按下或未按代码。

我不使用C / C ++,所以这是javascript我用这对我的GAMEBOY模拟器网站的字符串部分可能是错的,但实际的逐位码是在几乎所有的编程语言普及,只有我见过的区别是Visual Basic中&会所谓AND那里。

function WhatControlsPressed(controlsByte) {
    var controlsPressed = " ";
    if (controlsByte & 1) {
        controlsPressed = controlsPressed + "up "
    }
    if (controlsByte & 2) {
        controlsPressed = controlsPressed + "down "
    }
    if (controlsByte & 4) {
        controlsPressed = controlsPressed + "left "
    }
    if (controlsByte & 8) {
        controlsPressed = controlsPressed + "right "
    }
    if (controlsByte & 16) {
        controlsPressed = controlsPressed + "a "
    }
    if (controlsByte & 32) {
        controlsPressed = controlsPressed + "b "
    }
    if (controlsByte & 64) {
        controlsPressed = controlsPressed + "select "
    }
    if (controlsByte & 128) {
        controlsPressed = controlsPressed + "start "
    }
    return controlsPressed;
}

如何设置一个单独的控制被压? 好,你要记住你用来做什么的控制我会做这样的事情该按位号码

#DEFINE UP 1
#DEFINE DOWN 2
#DFFINE LEFT 4
#DEFINE RIGHT 8

因此,可以说你按下UpA曾经在那么您按116

你让保存的所有控件1个字节可以说,

unsigned char ControlsPressed = 0;

所以没有什么是现在按下,因为它是0。

ControlsPressed |= 1; //Pressed Up
ControlsPressed |= 16; //Pressed A

所以,是的ControlsPressed现在将持有数17 ,你可能会想只是1+16 ,这正是它笑,但耶水的事情,你不能让它回到它是由它摆在首位的基本价值观利用基本的数学。

但是,是的,你可以改变1716 ,然后咣当你放手关闭向上箭头,只是按住A按钮。

但是,当你按住按钮地段的价值得到那么大可以说。 1+4+16+128 = 149

所以,你不记得你添加什么了,但你知道的值是149 ,你会如何取回钥匙了吗? 以及它很容易啊刚开始减去最高数量,你可以找到你的控件使用低则149和你,如果它是当你减去它,然后它没有按下更大。

是啊,在这一点上,你想啊,我可以做一些循环和做这个东西,但是这一切都没有需要做的是做对飞有内置的命令。

这是你如何unpress任何按钮。

ControlsPressed = ControlsPressed AND NOT (NEGATE) Number

在C / C ++ / Java脚本,你可以使用这样的

ControlsPressed &= ~1; //Let go of Up key.
ControlsPressed &= ~16; //Let go of A key.

还有什么说这是你需要了解的东西按位的一切。

编辑:

我没有解释按位移位运算符<<>>我真的不知道如何在一个基本水平的解释。

但是,当你看到这样的事情

int SomeInteger = 123;
print SomeInteger >> 3;

也就是说最多有一个右移操作者习惯和有它的移位3个比特到右侧。

什么它实际上是除以2〜3。因此,在基本的数学它真的这样做的动力

SomeInteger = 123 / 8;

所以,现在你知道右移>>是同样的事情,以2的幂现在左移分度值<<逻辑上意味着你是乘以2的幂的值。

比特移位大多用于包装2点不同的数据类型一起和以后提取它们。 (我认为这是最常用的位移位)。

假设你有X / Y坐标在游戏中每个坐标只能去有限的值。 (这只是一个例子)

X: (0 to 63)
Y: (0 to 63)

你也知道,X,Y必须被存储到一些小的数据类型。 我认为非常紧凑(无缺口)。

(这可能需要一些反向工程弄清楚到底或只是阅读手册)。 有可能是有差距用于保留位或某些未知的信息。

但随着搬到这里来这样既可以容纳一共有64种不同的组合。

因此,无论XY各取6位,总共12位用于两者。 所以总共2位存入每个字节。 (4个比特保存在总)。

  X | Y 

[1 2 4 8 16 32] | [1 2 4 8 16 32]
[1 2 4 8 16 32 64] [128 1 2 4 8 [16 32 64 128]

所以,你需要使用bitshifting能够正确地储存信息。

这里是你如何存储它们

int X = 33;
int Y = 11;

由于每个坐标取6位,这将意味着你得换了6留给每个号码。

int packedValue1 = X << 6; //2112
int packedValue2 = Y << 6; //704
int finalPackedValue = packedValue1 + packedValue2; //2816

所以,是的终值将是2816

现在你得到的值回2816做相反方向的相同的移位。

2816 >> 6 //Gives you back 44. lol.

所以是与水的问题再次发生,你有44(33 + 11),并没有办法把它找回来,这个时候你不能依靠2的权力,以帮助你。

我用很凌乱的代码来告诉你为什么你一定是故意来抵御对未来错误的复杂化。

反正回到上面的每6位的坐标是你必须做的是把6中,添加它。

所以现在你有66+6=12

int packedValue1 = X << 6; //2112
int packedValue2 = Y << 12; //45056
int finalPackedValue = packedValue1 + packedValue2; //47168

所以,是的终值现在更大47168 ..但ATLEAST现在你必须不惜一切又回到价值观没有问题。 只有一点要记住,你必须首先做方向相反的最大的变化。

47168 >> 12; //11

现在,你必须弄清楚什么大数字11是由这样你转移回留下的12倍。

11 << 12; //45056

从最初的总和减去

//47168 - 45056 = 2112

现在,您可以通过6完成右移。

2112 >> 6; //33

您现在有两个值回..

你可以做包装部分多用按位命令上面添加控件在一起更容易。

int finalPackedValue = (X << 6) | (Y << 12);


文章来源: GameBoy Advance objects not being displayed in correct place when player is moving