Непоследовательное поведение между двумя игроками в игре - PullRequest
1 голос
/ 23 сентября 2011

Я делал игру для презентации на английском (я знаю, верно?), И у меня недавно было несколько странных проблем.

На этом этапе есть два поля для игроков, которыеможет стрелять пулями.Когда пули попадают на боковую сторону экрана или среднюю границу, они должны исчезнуть и, возможно, быть использованы повторно позже.

Проблема заключается, прежде всего, в том, что когда игрок стреляет вверх или вниз, пули исчезают вверх / низ / середина экрана, как они должны.Если игрок 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 после нее.

1 Ответ

0 голосов
/ 23 сентября 2011

Похоже, что deleteBullet может вызываться дважды на одном и том же i внутри цикла for (i=0...) в moveBullets.Я думаю, что это может произойти, если пуля движется по диагонали.Я не уверен, что это является причиной проблемы, хотя.

Первый английский вопрос, который я видел по stackoverflow!

...