Я делал игру для презентации на английском (я знаю, верно?), И у меня недавно было несколько странных проблем.
На этом этапе есть два поля для игроков, которыеможет стрелять пулями.Когда пули попадают на боковую сторону экрана или среднюю границу, они должны исчезнуть и, возможно, быть использованы повторно позже.
Проблема заключается, прежде всего, в том, что когда игрок стреляет вверх или вниз, пули исчезают вверх / низ / середина экрана, как они должны.Если игрок 2 стреляет несколько в сторону, как только один удар, они, кажется, все исчезают (только пули игрока 2), и мне нужно подождать несколько секунд, прежде чем он снова начнет стрелять.
Основная проблематем не менее, даже если я использую точно такой же код, измененный для игрока 1, а не для игрока 2, когда первый выстрел игрока 1, попадающий в боковую часть экрана, попадает в него, он вызывает ошибку программы.
ДЕЙСТВИТЕЛЬНО странныйДело в том, что в какой-то момент это произошло, и, ничего не меняя, я запустил его снова, и все заработало идеально.Это может быть связано с порядком пуль между игроком 1 и 2 или даже тем, где я стреляю первым.Я пытался воссоздать его, но пока безрезультатно.
Просто заметка, прежде чем пытаться скомпилировать код, я использовал оболочку, которую сделал, чтобы сделать окно с легкостью.Я записал, что происходит за кулисами с комментариями ///, поэтому добавление этой информации в любой метод, который вы используете для создания своих окон, также будет работать.
Проблемные области перечислены внизу:
///Works best on 1280x1024 resolution
///1 vs 1 splitscreen game that involves flying around and shooting things
///angles start at 0 facing upwards and increase clockwise
#include <window.h> //incomplete wrapper, but works perfectly for quick, easy window creation
#define _USE_MATH_DEFINES //for M_PI
#include <cmath> //for M_PI
#include <iostream> //used for debugging
using std::cout; //output
struct Actions //actions a player can take
{
bool up; //if player is moving in these 4 directions, they will be true
bool left;
bool down;
bool right;
bool shoot; //if player is shooting, this will be true
};
struct Player //a player
{
Player() {};
void fire(); //fire a bullet
void checkActions (HWND); //check what actions player is taking
double x; //position (centre of square)
double y;
double angle; //angle (might add diagonals so...)
int pnum; //player number (0 or 1)
COLORREF colour; //player's colour
Actions action; //player's actions
};
struct Bullet //a bullet
{
double x; //position (centre of square)
double y;
Player owner; //owner of bullet
int index; //bullet's index in array
double angle; //bullet's angle
};
Player *p = new Player[2]; //2 players
Bullet **bullet; //2d array of bullets
int bcount[2] = {0}; //number of bullets for each player
int btime [2] = {0}; //timer for bullets
const double PLSIZE = 10; //player size = 20x20 square (10 from centre outwards)
const double BSIZE = 2; //bullet size = 4x4 square
const double SPEED = 1; //player's moving speed is 1
const int BDELAY = 100; //delay between bullets is 100ms
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); //window procedure
void OnPaint (HDC, HWND); //painting function
void moveBullets (HWND); //calculates bullet positions
void deleteBullet (int, int); //"deletes" a bullet
int main() //main function
{
//hide(console()); //hides console window (currently showing for debugging)
bullet = new Bullet*[2]; //create bullet array of 1000/player (I'll size down the 1000 later)
bullet[0] = new Bullet[1000];
bullet[1] = new Bullet[1000];
p[0].x = 630; //player 1's position
p[0].y = 250;
p[0].colour = RGB(255,0,0); //player 1 is red
p[0].pnum = 0; //player 1's number is 0
p[0].angle = 0; //face upwards
p[0].action = {0}; //player 1 is doing nothing
p[1].x = 630; //player 2's position
p[1].y = 750;
p[1].colour = RGB(0,0,255); //player 2 is blue
p[1].pnum = 1; //player 2's number is 1
p[1].angle = 0; //face upwards
p[1].action = {0}; //player 2 is doing nothing
Window window; //create window object (part of wrapper, sets default values for class and window)
///background = (HBRUSH)COLOR_WINDOW
///class name = "Default Wrapper Class"
///hInstance = GetModuleHandle (NULL)
///all others are standard default or 0
window.createClass(WndProc); //create class using earlier-mentioned window procedure
window.setStyle(WS_OVERLAPPEDWINDOW | WS_MAXIMIZE); //set window style to overlapped and maximized
window.setTitle (L"Word Blaster"); //set window title to "Word Blaster" (it's an English project, shush)
///x/y/width/height = CW_USEDEFAULT
///class name = other class name
///hInstance = GetModuleHandle (NULL)
///all others are standard default or 0
HWND hwnd = window.createWindow(); //create window
MSG msg; //message loop
while(GetMessage(&msg,0,0,0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) //window proc
{
HDC hdc; //hdc for painting
PAINTSTRUCT ps; //paintstruct for painting
bool ret = false; //return value (you'll see later)
switch(msg)
{
case WM_CREATE: //currently not in use
break;
case WM_KEYDOWN: //check for pressed keys
switch (wParam) //keycode
{
case 0x57: //'w'
p[0].action.up = true; //player 1 wants to move up (couldn't just change position here or no diagonal movement)
break;
case 0x41: //'a', left
p[0].action.left = true;
break;
case 0x53: //'s', down
p[0].action.down = true;
break;
case 0x44: //'d', right
p[0].action.right = true;
break;
case 0x20: // space, shoot
p[0].action.shoot = true;
break;
case VK_UP: //up arrow, player 2 up
p[1].action.up = true;
break;
case VK_LEFT: //left arrow
p[1].action.left = true;
break;
case VK_DOWN: //down arrow
p[1].action.down = true;
break;
case VK_RIGHT: //right arrow
p[1].action.right = true;
break;
case VK_RETURN: //either enter key, p2 shoot
p[1].action.shoot = true;
break;
}
break;
case WM_KEYUP: //check for unpressed keys
switch (wParam)
{
case 0x57: //'w', player 1 should stop moving up
p[0].action.up = false;
break;
case 0x41: //all same order as above
p[0].action.left = false;
break;
case 0x53:
p[0].action.down = false;
break;
case 0x44:
p[0].action.right = false;
break;
case 0x20: // space
p[0].action.shoot = false;
break;
case VK_UP:
p[1].action.up = false;
break;
case VK_LEFT:
p[1].action.left = false;
break;
case VK_DOWN:
p[1].action.down = false;
break;
case VK_RIGHT:
p[1].action.right = false;
break;
case VK_RETURN:
p[1].action.shoot = false;
break;
}
break;
case WM_PAINT: //draw on screen
hdc = BeginPaint (hwnd, &ps); //prepare window for drawing
OnPaint (hdc,hwnd); //draw
EndPaint (hwnd, &ps); //finish drawing
break;
case WM_CLOSE: //if ready to close
show(console()); //show console window in case it doesn't close
end(); //close console window (PostMessage (GetConsoleWindow(),WM_CLOSE,0,0))
DestroyWindow(hwnd); //close main window
break;
case WM_DESTROY: //window is closing
PostQuitMessage(0); //post WM_QUIT to end (probably won't get here since console closes earlier)
break;
case WM_ERASEBKGND: //if background is going to be erased, don't let it (causes flicker)
ret = true; //hold that thought for a bit
break;
}
p[0].checkActions(hwnd); //check player 1's actions
p[1].checkActions(hwnd); //check player 2's actions
moveBullets (hwnd); //move any bullets
InvalidateRect (hwnd,NULL,true); //update window
Sleep (1); //delay a bit
if (!ret) return DefWindowProc(hwnd, msg, wParam, lParam); //if WM_ERASEBKGND wasn't called, take default action
}
void Player::fire() //fire a bullet
{
bullet [pnum][bcount[pnum]].x = x; //bullet starts in player's centre
bullet [pnum][bcount[pnum]].y = y;
bullet [pnum][bcount[pnum]].owner = *this; //owner of bullet is the object calling this function
bullet [pnum][bcount[pnum]].index = bcount[pnum]; //index of bullet is the number of bullets for player
bullet [pnum][bcount[pnum]].angle = angle; //angle of bullet is player's angle
while (
(bullet[pnum][bcount[pnum]].x - BSIZE < x + PLSIZE && bullet[pnum][bcount[pnum]].x - BSIZE > x - PLSIZE //left side of bullet inside player OR
|| bullet[pnum][bcount[pnum]].x + BSIZE < x + PLSIZE && bullet[pnum][bcount[pnum]].x + BSIZE > x - PLSIZE) //right side in player --- AND ---
&& (bullet[pnum][bcount[pnum]].y - BSIZE < y + PLSIZE && bullet[pnum][bcount[pnum]].y - BSIZE > y - PLSIZE //top in player OR
|| bullet[pnum][bcount[pnum]].y + BSIZE < y + PLSIZE && bullet[pnum][bcount[pnum]].y + BSIZE > y - PLSIZE) //bottom in player
)
{
bullet[pnum][bcount[pnum]].x += sin (bullet[pnum][bcount[pnum]].angle * M_PI / 180); //start moving bullet until it's out
bullet[pnum][bcount[pnum]].y -= cos (bullet[pnum][bcount[pnum]].angle * M_PI / 180);
}
btime [pnum] = GetTickCount(); //set up bullet delay for that player
++bcount[pnum]; //increase number of bullets for that player
}
void Player::checkActions (HWND hwnd) //check player's actions
{
RECT r;
GetClientRect (hwnd, &r); //get canvas space
if (action.up) //if moving up
{
y -= SPEED; //change y position
angle = 0; //change angle
if (pnum == 0) //if player 1
{
if (y - PLSIZE < 1) y = PLSIZE + 1; //check top of screen boundary
}
else //if player 2
{
if (y - PLSIZE < r.bottom / 2 + 5) y = r.bottom / 2 + 5 + PLSIZE; //check middle boundary
}
}
if (action.left) //if moving left
{
x -= SPEED; //change x position
angle = 270; //change angle
if (x - PLSIZE < 1) x = PLSIZE + 1; //check left of screen boundary
}
if (action.down) //down is opposite of up
{
y += SPEED;
angle = 180;
if (pnum == 0)
{
if (y + PLSIZE > r.bottom / 2 - 5) y = r.bottom / 2 - 5 - PLSIZE;
}
else
{
if (y + PLSIZE > r.bottom) y = r.bottom - PLSIZE;
}
}
if (action.right) //right is opposite of left
{
x += SPEED;
angle = 90;
if (x + PLSIZE > r.right) x = r.right - PLSIZE;
}
if (action.shoot && GetTickCount() - btime [pnum] > BDELAY) fire(); //if player wants to shoot and enough time has passed, fire bullet
}
void OnPaint (HDC hdc, HWND hwnd) //draw stuff
{
RECT r;
GetClientRect (hwnd, &r); //get canvas area
HDC buffer = CreateCompatibleDC (hdc); //create buffer DC
HBITMAP bitmap = CreateCompatibleBitmap (hdc,r.right,r.bottom); //create buffer bitmap
HBITMAP oldBM = (HBITMAP)SelectObject (buffer, bitmap); //create another bitmap
HBRUSH player1brush = CreateSolidBrush(p[0].colour); //player 1's brush
HBRUSH player2brush = CreateSolidBrush(p[1].colour); //player 2's brush
HBRUSH blackBrush = CreateSolidBrush (RGB(0,0,0)); //black brush
HPEN /*player1*/pen = CreatePen (PS_NULL,1,RGB(255,0,0)); //don't need pen
BitBlt(buffer,0,0,r.right,r.bottom,NULL,0,0,WHITENESS); //erase bitmap background
SelectObject(buffer,pen); //select pen (since I need one to do anything)
SelectObject (buffer, blackBrush); //select black brush
Rectangle (buffer, 0, r.bottom / 2 - 5, r.right, r.bottom / 2 + 5); //draw middle line
// MoveTo () //these comments are because I was about to change the graphics to ships
SelectObject (buffer,player1brush); //select player 1's brush
Rectangle (buffer,p[0].x-PLSIZE,p[0].y-PLSIZE,p[0].x+PLSIZE,p[0].y+PLSIZE); //draw player 1
SelectObject (buffer,player2brush); //do the same for p2
Rectangle (buffer,p[1].x-PLSIZE,p[1].y-PLSIZE,p[1].x+PLSIZE,p[1].y+PLSIZE);
if (bcount[0] > 0) //if p1 has a bullet
{
SelectObject (buffer, blackBrush); //select black brush
for (int i = 0; i < bcount[0]; ++i) //draw bullet(s)
{
Ellipse (buffer, bullet [0][i].x - BSIZE, bullet [0][i].y - BSIZE, bullet [0][i].x + BSIZE, bullet [0][i].y + BSIZE);
}
}
if (bcount[1] > 0) //same goes for p2
{
SelectObject (buffer, blackBrush);
for (int i = 0; i < bcount[1]; ++i)
{
Ellipse (buffer, bullet [1][i].x - BSIZE, bullet [1][i].y - BSIZE, bullet [1][i].x + BSIZE, bullet [1][i].y + BSIZE);
}
}
BitBlt(hdc, 0,0, r.right , r.bottom, buffer, 0,0, SRCCOPY); //copy buffer bitmap to window
DeleteObject (player1brush); //delete stuff
DeleteObject (player2brush);
DeleteObject (pen);
SelectObject (buffer, oldBM);
DeleteObject (bitmap);
DeleteDC(buffer);
}
void moveBullets (HWND hwnd) //move the bullets ***PROBLEM AREA***
{
RECT r;
GetClientRect (hwnd, &r); //get canvas area
if (bcount[0] > 0) //if p1 has bullet(s)
{
for (int i = 0; i < bcount[0]; ++i) //go through p1's bullets
{
///DOESN'T WORK
bullet [0][i].x += sin (bullet [0][i].angle * M_PI / 180); //move the bullet horizontally
if (bullet [0][i].x - BSIZE < 1 || bullet [0][i].x + BSIZE > r.right) //if out of bounds
{
deleteBullet (0, bullet [0][i].index); //delete the bullet
--i; //if bullet [2] was deleted, bullet [2] will now be the old bullet [3] so recheck this one next time
}
///WORKS PERFECTLY
bullet [0][i].y -= cos (bullet [0][i].angle * M_PI / 180); //do same for y, including middle border
if (bullet [0][i].y - BSIZE < 1 || bullet [0][i].y + BSIZE > r.bottom / 2 - 5)
{
deleteBullet (0, bullet [0][i].index);
--i;
}
}
}
if (bcount[1] > 0) //exact same thing (I checked a LOT) for p2
{
for (int i = 0; i < bcount[1]; ++i)
{
///WORKS PERFECTLY (at least in the p1 sense, there is a slight problem)
bullet [1][i].x += sin (bullet [1][i].angle * M_PI / 180);
if (bullet [1][i].x - BSIZE < 1 || bullet [1][i].x + BSIZE > r.right)
{
deleteBullet (1, bullet [1][i].index);
--i;
}
///WORKS PERFECTLY
bullet [1][i].y -= cos (bullet [1][i].angle * M_PI / 180);
if (bullet [1][i].y - BSIZE < r.bottom / 2 + 5 || bullet [1][i].y + BSIZE > r.bottom)
{
deleteBullet (1, bullet [1][i].index);
--i;
}
}
}
}
void deleteBullet (int player, int index) //delete bullet ***PROBLEM AREA***
{
if (index != bcount [player] - 1) //if it isn't the last bullet
{
for (int j = index; j < bcount[player] - 1; ++j) //go from here to the end of the current bullets - 1
{
bullet [player][j] = bullet [player][j+1]; //copy the next bullet into this spot
--bullet [player][j].index; //change the index of the bullet since it was moved back one
}
}
--bcount [player]; //lessen the bullet count, this is all that's needed if it's the last bullet
}
Любая помощь с указанными проблемами или с чем-то еще, что вы заметите, будет принята с благодарностью.
РЕДАКТИРОВАТЬ: одна сторона, которую я забыл спросить, была ли лучший способ непрерывно делать вещинапример, перемещать маркеры, чем помещать все это вне переключателя в оконную процедуру и DefWindowProc
после нее.