当玩家移动的GameBoy Advance的对象不会显示在正确的位置 [英] GameBoy Advance objects not being displayed in correct place when player is moving

查看:183
本文介绍了当玩家移动的GameBoy Advance的对象不会显示在正确的位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

不过,我有麻烦时,我显示箱体可以拿起和操纵的水平。每当玩家移动,屏幕上的方块的位置似乎渐行渐远从一级的实际位置。这就像在哪里绘制立方体为单个帧同步的 - 当我暂停当玩家移动时,框显示在完全正确的位置,本场比赛,但是当我取消暂停,漂出来的地方,直到球员站动了。

我的课的简短说明 - 有一个基类叫做对象定义(X,Y)的位置,宽度和高度,还有它的速度分量继承对象,并将实体类和字符类,继承实体,并增加了运动功能。我的球员是一个字符对象,而我要拿起立方体是实体对象的数组。无论是球员和立方体阵列是二级类,它也继承了对象的成员。

我怀疑问题出在最后code样品中,但是,对于全面COM $ P $什么,我试图做我已经制定了样品中稍微逻辑顺序。phension

下面是水平的截头:

 类等级:公共对象
{
    私人:
        //数据
        INT backgroundoffsetx;
        INT backgroundoffsety;

        //方法
        无效ApplyEntityOffsets();
        无效DetermineBackgroundOffsets();

    上市:
        //数据
        枚举{MAXCUBES = 20};

        实体立方体[MAXCUBES]
        性格的球员;
        INT numofcubes;

        //方法
        水平();
        void绘制();
        无效DrawBackground(尺寸);
        无效UpdateLevelObjects();
};
 

...和实体:

 实体类:公共对象
{
    私人:
        //方法
        INT GetScreenAxis(INT和放大器;,INT和放大器;,const int的,INT和放大器;,const int的);

    上市:
        //数据
        INT drawx; //凡实体的x位置是相对于屏幕
        INT drawy; //当实体的y位置是相对于屏幕

        //方法
        无效SetScreenPosition(INT和放大器;,INT和放大器;);
};
 

下面是我的主游戏循环中的相关部分:

  //主回路
而(真)
{
    ...

    level.MoveObjects(按钮);
    level.Draw();
    level.UpdateLevelObjects();

    ...
}
 

暂停时精灵显示在正确的地方的方式,因为,我是pretty的确保问题不在于 MoveObjects(),它决定播放器的poitions和立方体中的水平相对于水平。使叶片绘图() UpdateLevelObjects()

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

  / **
绘制水平的背景;
* /
无效级别:: DrawBackground(尺寸curdimension)
{
    ...

    //平台
    的for(int i = 0; I< numofplatforms;我++)
    {
        对于(INT Y =平台[I] .Gety()/ 8; Y<平台[I] .GetBottom()/ 8; Y ++)
        {
            对于(INT X =平台[I] .Getx()/ 8,X<平台[I] .GetRight()/ 8; X ++)
            {
                如果(X LT; 32)
                {
                    如果(γ&所述; 32)
                    {
                        SetTile(25,X,Y,103);
                    }
                    其他
                    {
                        SetTile(27,X,Y  -  32,103);
                    }
                }
                其他
                {
                    如果(γ&所述; 32)
                    {
                        SetTile(26,X  -  32,Y,103);
                    }
                    其他
                    {
                        SetTile(28,X  -  32,Y  -  32,103);
                    }
                }
            }
        }
    }
}
 

这必然要求解释的一些数额。我的平台以像素为单位,但在8×8像素的瓷砖显示,所以我必须把他们的大小这个循环。 SetTile()首先需要screenblock号码。我使用的显示平台的背景层为64×64瓦,因此需要对每一个32×32瓦×2 screenblocks来显示它们。该screenblocks编号为25-28。 103是我的tilemap的瓦数。

下面的 UpdateLevelObjects()

  / **
更新级别的所有GBA对象
* /
无效级别:: UpdateLevelObjects()
{
    DetermineBackgroundOffsets();
    ApplyEntityOffsets();

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

    ...

    // code这台播放器的位置(drawx,drawy);

    //绘制立方体
    的for(int i = 0; I< numofcubes;我++)
    {
        // code这台魔方[I]位置(drawx,drawy);
    }
}
 

REG_BG 位是GBA的允许背景层被垂直和水平地通过的像素数的偏移的寄存器。这些偏移在 DetermineBackgroundOffsets首先计算()

  / **
计算屏幕的基于其中播放器处于水平偏移
* /
无效级别:: DetermineBackgroundOffsets()
{
    如果(player.Getx()&所述; SCREEN_WIDTH / 2)//如果播放器屏幕的不到一半的宽度远离水平的左侧壁
    {
        backgroundoffsetx = 0;
    }
    否则如果(player.Getx()>宽度 - (SCREEN_WIDTH / 2))//如果播放器屏幕的不到一半的宽度远离水平的右壁上
    {
        backgroundoffsetx =宽度 -  SCREEN_WIDTH;
    }
    别的//如果播放器处于水平的中间
    {
        backgroundoffsetx =  - ((SCREEN_WIDTH / 2) -  player.Getx());
    }

    如果(player.Gety()&所述; SCREEN_HEIGHT / 2)
    {
        backgroundoffsety = 0;
    }
    否则,如果(player.Gety()>高度 - (SCREEN_HEIGHT / 2))
    {
        backgroundoffsety =身高 -  SCREEN_HEIGHT;
    }
    其他
    {
        backgroundoffsety =  - ((SCREEN_HEIGHT / 2) -  player.Gety());
    }
}
 

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

下面的 ApplyEntityOffsets

  / **
确定保持玩家在屏幕中间的偏移
* /
无效级别:: ApplyEntityOffsets()
{
    //播放器偏移
    player.drawx = player.Getx() -  backgroundoffsetx;
    player.drawy = player.Gety() -  backgroundoffsety;

    //立方偏移
    的for(int i = 0; I< numofcubes;我++)
    {
        立方体[I] .SetScreenPosition(backgroundoffsetx,backgroundoffsety);
    }
}
 

这基本上居中在屏幕上播放时,它是在电平的中间,并允许它移到边缘的时候,屏幕碰撞到水平的边缘。至于立方体:

  / **
确定实体相对一个的x和y位置到屏幕
* /
无效实体:: SetScreenPosition(INT和放大器; backgroundoffsetx,INT和放大器; backgroundoffsety)
{
    drawx = GetScreenAxis(X,宽度,512,backgroundoffsetx,SCREEN_WIDTH);
    drawy = GetScreenAxis(Y,高度,256,backgroundoffsety,SCREEN_HEIGHT);
}
 

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

  / **
设置沿实体相对的轴线位置在屏幕上的位置
* /
INT实体:: GetScreenAxis(INT和放大器;轴,INT和放大器; dimensioninaxis,const int的OBJECT_OFFSET,
                            INT和放大器; backgroundoffsetaxis,const int的SCREEN_DIMENSION)
{
    INT在newPosition;
    布尔onawkwardedgeofscreen = FALSE;

    //如果实体的位置是部分关闭屏幕-ve方向
    如果(轴 -  backgroundoffsetaxis< dimensioninaxis)
    {
        在newPosition =轴 -  backgroundoffsetaxis + OBJECT_OFFSET;
        onawkwardedgeofscreen = TRUE;
    }
    其他
    {
        在newPosition =轴 -  backgroundoffsetaxis;
    }

    如果((在newPosition> SCREEN_DIMENSION)及&安培;!onawkwardedgeofscreen)
    {
        在newPosition = SCREEN_DIMENSION; //摆脱的出现在屏幕上出问题广场
    }

    返回在newPosition;
}
 

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

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


编辑:方式GBA的对象被更新的设置播放器和皂的位置如下:

 的for(int i = 0; I< numofcubes;我++)
{
    的setObject(立方体[I] .GetObjNum()
      ATTR0_SHAPE(0)| ATTR0_8BPP | ATTR0_REG | ATTR0_Y(立方体[I] .drawy)
      ATTR1_SIZE(0)| ATTR1_X(立方体[I] .drawx)
      ATTR2_ID8(0)| ATTR2_PRIO(2));
}
 

解决方案

我会解释这个答案怎么位运算符的工作,以及如何一个数字可以说,以0至255(256组合)的可能值一个字节包含所有GBA控制presses。这类似于你的X / Y位置的问题。

控件

向上 - 向下 - 左 - 右 - A - B - 选择 - 开始

这些都是游戏男孩颜色控制,我认为GameBoy的高级有更多的控制。 所以总共8控制。 每个控制可以是pressed(按住)或没有pressed。 这将意味着每个控制应该只使用了一些 1 0 。 由于 1 0 只需要1比特的信息。在一个字节可以存储多达8个不同的位,适合所有的控件。

现在,你可能会想我怎么能结合在一起,加入的东西?是的,你可以做到这一点,但它使得它很复杂,难以理解,它给了你这个问题。

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

但随着位运算,它使用数学计算出这一点恰恰是一个 1 0 在整个比特流(列表)。

你这样做第一件事就是你给每一位来控制。 每一位在2二进制的倍数,所以你只要保持加倍的价值。

向上 - 向下 - 左 - 右 - A - B - 选择 - 开始
1 - 2 - 4 - 8 - 16 - 32 - 64 - 128

还有位操作不仅用于找出哪一位是 1 0 你也可以使用他们对某些东西结合在了一起。控制做好这一点,因为你可以preSS和持有多个按钮一次。

下面是code我用它来找出哪些是pressed与否pressed。

我不使用C / C ++,所以这是的JavaScript 我用这对我的GAMEBOY模拟器网站的字符串部分可能是错的,但实际按位code是普遍在几乎所有的编程语言,只有我见过的区别是Visual Basic中的&安培; 将被称为有。

 函数WhatControls pressed(controlsByte){
    无功控制pressed =;
    如果(controlsByte&安培; 1){
        控制pressed =控制pressed +向上
    }
    如果(controlsByte和2){
        控制pressed =控制pressed +下调
    }
    如果(controlsByte和4){
        控制pressed =控制pressed +左
    }
    如果(controlsByte和8){
        控制pressed =控制pressed +右
    }
    如果(controlsByte及16条){
        控制pressed =控制pressed +一
    }
    如果(controlsByte&安培; 32){
        控制pressed =控制pressed +B
    }
    如果(controlsByte&安培; 64){
        控制pressed =控制pressed +选择
    }
    如果(controlsByte&安培; 128){
        控制pressed =控制pressed +启动
    }
    返回控制pressed;
}
 

如何设置一个单独的控制是pressed?同时你要记住它逐位号码,你用什么控制我会做这样的事情

  #DEFINE UP 1
#DEFINE下降2
#DFFINE LEFT 4
#DEFINE RIGHT 8
 

因此​​,可以说你preSS下降向上 A 一下子让你pressed 1 16

您做1个字节保存所有的控制可以说

  unsigned char型控制pressed = 0;
 

所以,没有什么是现在pssed $ P $,因为它是0。

 控制pressed | = 1; // pressed最多
自控pressed | = 16; // pressed一个
 

所以,是的控制pssed $ P $ 现在将持股数 17 你可能会想刚 1 + 16 这正是它大声笑但是,是水的事情,你不能拿回来给它,使得它在使用基本的数学第一名的基本价值观。

但是,是的,你可以改变 17 16 和你放手离开箭头,只是抱着巴姆按下 A 按钮。

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

所以,你不记得你添加的什么了,但你知道这个值是 149 你怎么会现在拿回钥匙?那么它的pretty的容易耶刚开始减去的最高数,你可以找到你的控件使用的是较低的则 149 ,你如果当你减掉它,然后就是大它不是pressed下降。

是啊,在这一点上,你想啊,我可以做一些循环,做这个东西,但是这一切都没有需要做的工作还有内置的做到这一点的飞行指令。

这是你如何取消preSS任何按钮。

 控制pressed =控制pressed AND NOT(NEGATE)数量
 

在C / C ++ / Java脚本可以使用这样的事情

 控制pressed和放大器; =〜1; //放开键。
控制pressed和放大器; =〜16; //放开的一个关键。
 

还能说什么那是你需要了解的按位的东​​西应有尽有。

编辑:

我没有解释按位移动运营商<< >> 我真不知道如何解释这一个基本的水平。

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

  INT SomeInteger = 123;
打印SomeInteger>> 3;
 

这那里是一个右移运算符习惯那里,它的转向3位到右侧。

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

  SomeInteger = 123/8;
 

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

位换档主要是用来装2个不同的数据类型一起,以后提取出来。 (我认为这是最常用的位移位)。

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

  X:(0〜63)
Y:(0至63)
 

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

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

但在这里一起移动,以便既可以容纳一共有64种不同的组合。

因此​​,无论 X 各占6位,12位共两个。 因此,一共有2位被保存每个字节。 (保存在总共4位)。

  X | ÿ
 

     

[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 //给你回44.笑。
 

所以是与水的问题再次发生,你有44(33 + 11),也没有办法把它找回来,这时候你可以不依赖于2的幂为您排忧解难。

我用很凌乱code来告诉你为什么,你必须把它复杂化的宗旨,以抵御今后的错误。

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

所以,现在你有 6 6 + 6 = 12

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

所以是最后的值现在更大47168。但ATLEAST现在你不会有任何问题,都取回值。要记住你唯一必须做方向相反的第一个最大的转变。<​​/ P>

  47168&GT;&GT; 12; // 11
 

现在,你必须弄清楚什么大数字11是由这样你却将它后面留下12次。

  11 LT;&LT; 12; // 45056
 

从原来的总和减去

  // 47168  -  45056 = 2112
 

现在,你可以用鼠标右键6完成转变。

  2112&GT;&GT; 6; // 33
 

您现在有两个值回..

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

  INT finalPackedValue =(X&LT;&LT; 6)| (Y&LT;&LT; 12);
 

This might take a while to explain - go grab a snack while you're reading this.

I am developing a 2D puzzle platforming game for the Gameboy Advance in C++ (I'm a fairly new programmer). Up until last night, I have been making a phyics engine (just some axis aligned bounding box stuff) which I was testing using a level which was the size of the GBA's screen. However, the final game will demand having a level which is bigger than the size of the screen, and so I have tried to implement a system which allows the screen of the GBA to follow the player, and as a result I have to draw everything on screen relative to the screen's offsets.

However, I am having trouble when I display cubes which can be picked up and manipulated in the level. Whenever the player moves, the locations of the cubes on screen appear to drift away from their actual positions in the level. It's like where the cubes are drawn is a single frame out of sync - when I pause the game when the player is moving, the boxes are displayed in exactly the right position, but when I unpause, they drift out of place until the player stops moving again.

A brief description of my classes - there is a base class called Object which defines (x, y) position and a width and height, there is an Entity class which inherits from Object and adds velocity components, and a Character class which inherits from Entity and adds movement functions. My player is a Character object, while the cubes I want to pick up are an array of Entity objects. Both the player and cubes array are members of the Level class, which also inherits from Object.

I suspect the problem lies in the last code sample, however, for full comprehension of what I am trying to do I have laid out the samples in a slightly more logical order.

Here are the truncated headers of Level:

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();
};

...and Entity:

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 &);
};

Here are the relevant parts of my main game loop:

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

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

    ...
}

Because of the way sprites are displayed in the correct places when paused, I'm pretty sure the problem does not lie in MoveObjects(), which determines the poitions of the player and cubes in the level relative to the level. So that leaves Draw() and UpdateLevelObjects().

Ok, Draw(). I'm providing this in the event that it is not my cubes that are being displayed incorrectly, but the level and platforms upon which they sit (I don't think this is the problem, but possibly). Draw() only calls one relevant function, 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);
                    }
                }
            }
        }
    }
}

This inevitably requires some amount of explaining. My platforms are measured in pixels, but displayed in tiles of 8x8 pixels, so I have to divide their sizes for this loop. SetTile() firstly requires a screenblock number. The background layer I am using to display the platforms is 64x64 tiles, and so requires 2x2 screenblocks of 32x32 tiles each to display them all. The screenblocks are numbered 25-28. 103 is the tile number in my tilemap.

Here's 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);
    }
}

The REG_BG bits are the registers of the GBA which allow the background layers to be offset vertically and horizontally by a number of pixels. Those offsets are first calculated in 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());
    }
}

Just to be clear, width refers to the width of the level in pixels, while SCREEN_WIDTH refers to the constant value of the width of the GBA's screen. Also, sorry for the lazy repetition.

Here's 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);
    }
}

Basically this centres the player on the screen when it is in the middle of the level, and allows it to move to edges when the screen bumps against the edge of the level. As for the cubes:

/**
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);
}

Bear with me - I will explain the 512 and 256 in a moment. Here's 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 (the 512 and 256) is a GBA specific thing - setting an object's x or y position to a negative number won't do what you intend normally - it messes up the sprite used to display it. But there's a trick: if you want to set a negative X position, you can add 512 to the negative number, and the sprite will appear in the right place (e.g. if you were going to set it to -1, then set it to 512 + -1 = 511). Similarly, adding 256 works for negative Y positions (this is all relative to the screen, not the level). The last if statement keeps the cubes displayed fractionally off the screen if they would normally be displayed further away, as trying to display them too far away results in glitchy squares appearing, again GBA specific stuff.

You are an absolute saint if you have come this far having read everything. If you can find what potentially might be causing the drifting cubes, I will be VERY grateful. Also, any tips to generally improve my code will be appreciated.


Edit: The way the GBA's objects are updated for setting player and the cubes' positions is as follows:

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));
}

解决方案

I will explain this answer how bitwise operators work and how one number lets say a byte with a possible value of 0 to 255 (256 combinations) holds all the GBA Control presses. Which is similar to your X/Y position problem.

The controls

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

Those are the GameBoy Color controls I think GameBoy Advanced has more controls. So a total of 8 controls. Each control can either be pressed (held down) or not pressed. That would mean the each control should only be using a number 1 or 0. Since 1 or 0 takes only 1 bit of information. In one byte you can store up to 8 different bits, which fits all the controls.

Now you may be thinking how can I combine them together by adding or something? yes you can do that but it makes it very complicated to understand and it gives you this problem.

Say you have a glass of water that's half empty and you add more water into it and you want to separate the newly added water from the old water.. you just can't do that because the water all became one water with no way to undo this (unless we label each water moleclue and we ain't aliens yet.. lol).

But with Bitwise operations it uses math to figure out which bit exactly is a 1 or 0 in the whole stream (list) of bits.

So first thing you do is you give each bit to a control. Each bit is in binary a multiple of 2, so you just keep doubling the value.

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

Also bitwise operations are not only used to figure out which bit is a 1 or 0 you could also use them to combine certain things together. Controls do this well since you can press and hold multiple buttons at once.

Here is the code I use to figure out which is pressed or not pressed.

I don't use C/C++ so this is javascript I used this for my gameboy emulator website the string part may be wrong but the actual bitwise code is universal on nearly all programing languages, only difference I seen is Visual Basic the & would be called AND there.

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;
}

How do you set a individual control to be pressed? well you have to remember which bitwise number you used for what control I would make something like this

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

So lets say you press down Up and A at once So you pressed 1 and 16

You make 1 byte that holds all the controls lets say

unsigned char ControlsPressed = 0;

So nothing is pressed now because it's 0.

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

So yeah the ControlsPressed will now be holding the number 17 you may be thinking just 1+16 which is exactly what it does lol but yeah the water thing you can't get it back to it's basic values that made it up in the first place using basic math.

But yeah you could change that 17 to 16 and bam you let go off the Up arrow and just holding down the A button.

But when you holding down lots of buttons the value gets so big lets say. 1+4+16+128 = 149

So you don't remember that what you added up but you know the value is 149 how will you get back the keys now? well it's pretty easy yeah just start subtracting the highest number you can find your controls use that is lower then 149 and you if it's bigger when you subtract it then it's not pressed down.

Yeah at this point you thinking yeah I could make some loops and do this stuff but it's all no needed to be done there is built-in commands that do this on the fly.

This is how you unpress any of the buttons.

ControlsPressed = ControlsPressed AND NOT (NEGATE) Number

In C/C++/Javascript you can use something like this

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

What else to say that's about everything you need to know about the bitwise stuff.

EDIT:

I didn't explain the bitwise shifting operators << or >> I really don't know how to explain this on a basic level.

But when you see something like this

int SomeInteger = 123;
print SomeInteger >> 3;

That up there is a shift right operator getting used there and it's shifting 3 bits to the right.

What it actually does is divide by 2 to the power of 3. So in basic math it's really doing this

SomeInteger = 123 / 8;

So now you know that shifting to the right >> is the same thing as dividing the value by powers of 2. Now shifting to the left << would logically mean you are multiplying the value by the powers of 2.

Bit shifting is mostly used to pack 2 different datatypes together and extract them later. (I think this is the most common use of the bit shift).

Say you have X/Y Coordinates in your game each coordinate can only go to a limited value. (This is just a example)

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

And you also know that X,Y must be stored into some small datatype. I assume very tightly packed (no gaps).

(this may take some reverse engineering to figure out exactly or just reading manuals). There could be gaps in there used for reserved bits or some unknown information.

But moving along here so both can hold a total of 64 different combinations.

So both X and Y each take 6 bits, 12 bits in total for both. So a total of 2 bits are saved for each byte. (4 bits saved in total).

 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]

So you need to use bitshifting to store information properly.

Here is how you store them

int X = 33;
int Y = 11;

Since each coordinate takes 6 bits that would mean you have to shift left by 6 for each number.

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

So yeah the final value will be 2816

Now you get the values back from 2816 doing the same shift in the opposite direction.

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

So yeah the problem with the water happened again you have 44 (33+11) and no way to get it back and this time you can't rely on the powers of 2 to help you out.

I used very messy code to show you why you must complicate it on purpose to fend of bugs in the future.

Anyways back to above its 6 bits per coordinate what you must do is take the 6 and add it there.

so now you have 6 and 6+6=12.

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

So yeah the final value is now bigger 47168.. But atleast now you will have no problems at all getting back the values. Only thing to remember you must do it in opposite direction biggest shift first.

47168 >> 12; //11

Now you have to figure out what big number 11 is made of so you shift it back left 12 times.

11 << 12; //45056

Subtract from original sum

//47168 - 45056 = 2112

Now you can finish the shift right by 6.

2112 >> 6; //33

You now got both values back..

You can do the packing part much easier with the bitwise command above for adding the controls up together.

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

这篇关于当玩家移动的GameBoy Advance的对象不会显示在正确的位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆