你制作了一个包含所有控件的字节unsigned char ControlsPressed = 0;所以现在没有按下任何东西,因为它是 0.ControlsPressed |= 1;//按下ControlsPressed |= 16;//按下A所以是的 ControlsPressed 现在将持有数字 17 你可能认为只是 1+16 这正是它所做的,哈哈但是,是的,您无法将水恢复到最初使用基本数学构成的基本价值观.但是,是的,您可以将 17 更改为 16,然后松开向上箭头并按住 A 按钮.但是当你按住很多按钮时,价值会变得如此之大.1+4+16+128 = 149所以你不记得你加起来了什么,但你知道值是 149 你现在如何取回钥匙?嗯,这很简单,是的,只需开始减去您可以找到的控件使用的最高数字,该数字低于 149,如果减去它时它更大,则它不会被按下.是的,此时您认为是的,我可以进行一些循环并执行这些操作,但这一切都不需要完成,因为内置命令可以即时执行此操作.这就是您按下任何按钮的方式.ControlsPressed = ControlsPressed AND NOT (NEGATE) 数字在 C/C++/Javascript 中你可以使用这样的东西ControlsPressed &= ~1;//松开向上键.ControlsPressed &= ~16;//松开A键.还有什么要说的,就是关于按位的东西你需要知道的一切. 我没有解释按位移位运算符 << 或 >> 我真的不知道如何在基本层面上解释这一点.但是当你看到这样的东西int SomeInteger = 123;打印 SomeInteger >>3;在那里使用了一个右移运算符,它向右移动了 3 位.它实际上做的是除以 2 的 3 次方.所以在基础数学中它真的是这样做的SomeInteger = 123/8;所以现在您知道向右移动 >>> 与将值除以 2 的幂是一回事.现在向左移动 << 逻辑上意味着您将值乘以 2 的幂.位移位主要用于将 2 种不同的数据类型打包在一起并在以后提取它们.(我认为这是位移位最常见的用法).假设您的游戏中有 X/Y 坐标,每个坐标只能达到一个有限的值.(这只是一个例子)X: (0 到 63)是:(0 到 63)而且您也知道 X,Y 必须存储在一些小数据类型中.我假设非常紧凑(没有间隙).(这可能需要一些逆向工程才能准确地找出或只是阅读手册).可能存在用于保留位或某些未知信息的间隙.但是在这里移动,所以两者都可以容纳总共 64 种不同的组合.所以 X 和 Y 各占 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]所以你需要使用位移来正确存储信息.这是您存储它们的方式int X = 33;整数 Y = 11;由于每个坐标需要 6 位,这意味着您必须为每个数字左移 6.intpackedValue1 = X 所以是的,最终值将是 2816现在你从 2816 取回值,在相反的方向做同样的转变.2816 >>6//还给你 44.lol.所以是的,水的问题再次发生了,你有 44 (33+11) 并且没有办法把它找回来,这次你不能依靠 2 的力量来帮助你.我使用了非常凌乱的代码来向您展示为什么您必须故意将其复杂化以防止将来出现错误.无论如何回到每个坐标 6 位以上,您必须做的是取 6 并将其添加到那里.所以现在你有 6 和 6+6=12.intpackedValue1 = X 所以是的,最终值现在更大了 47168.. 但至少现在你将完全没有问题要取回这些值.唯一要记住的是,您必须先以相反的方向进行最大的转变.47168 >>12;//11现在你必须弄清楚 11 是由什么大数字组成的,所以你把它向左移动 12 次.11 <<12;//45056从原和中减去//47168 - 45056 = 2112现在你可以完成右移 6.2112 >>6;//33您现在获得了两个值..使用上面的按位命令将控件添加在一起,您可以更轻松地完成打包部分.int finalPackedValue = (X 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 loopwhile (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 controlsUp - Down - Left - Right - A - B - Select - StartThose 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 - Start1 - 2 - 4 - 8 - 16 - 32 - 64 - 128Also 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 8So lets say you press down Up and A at once So you pressed 1 and 16You make 1 byte that holds all the controls lets sayunsigned char ControlsPressed = 0;So nothing is pressed now because it's 0.ControlsPressed |= 1; //Pressed UpControlsPressed |= 16; //Pressed ASo 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 = 149So 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) NumberIn C/C++/Javascript you can use something like thisControlsPressed &= ~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 thisint 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 thisSomeInteger = 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 themint 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; //2112int packedValue2 = Y << 6; //704int finalPackedValue = packedValue1 + packedValue2; //2816So yeah the final value will be 2816Now 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; //2112int packedValue2 = Y << 12; //45056int finalPackedValue = packedValue1 + packedValue2; //47168So 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; //11Now you have to figure out what big number 11 is made of so you shift it back left 12 times.11 << 12; //45056Subtract from original sum//47168 - 45056 = 2112Now you can finish the shift right by 6.2112 >> 6; //33You 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 对象未显示在正确的位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 08-23 11:55