sobota, 5 listopada 2016

Główna mechanika gry

Witam 

Dziś zajmiemy się ostatnią częścią naszej gry czyli klasą engine zacznijmy więc od pliku nagłówkowego
#include "pole.h"
#pragma once

class Engine
{
        //wskaźniki do okna i czcionki które otrzymamy z klasy game 
sf::RenderWindow *window;
sf::Font *font;
        //stworze tekstów odnośnie wyniku gry
sf::Text text;
sf::Text info;

 //stworzenie naszej planszy i planszy przeciwnika
    Pole pole[10][10];
    Pole pole_przec[10][10];


    enum Game{MARK,SHOT,OVER}; //zmienna wskazująca na jakim etapie gry jesteśmy
    Game state;
void update(); //aktualizuje informacje
void draw(); //rysuje wszystkie elementy
int mark=0; //wskazuje ile statków zaznaczyliśmy
void IAmark(); //zaznaczanie statków przez komputer
void IAshot(); //zgadywanie naszych statków przez komputer
int points=0,IApoints=0; //zmiene wskazujące ile statków zestrzeliliśmy


public:


// konstruktor i destruktor
    Engine(sf::RenderWindow &win, sf::Font &fon);
    ~Engine();

    void runEngine(); //główna funkcja obsługująca mechanikę

};

Teraz zajmijmy się plikiem engine.cpp

#include "pole.h"
#pragma once

#include "engine.h"
#include <Windows.h>
#include <cstdlib>
#include <ctime>
#include <cstdio>


using namespace sf;

Engine::Engine(sf::RenderWindow &win, sf::Font &fon)
{
    window = &win;
    font=&fon;

//sformatowanie wyświetlanych tekstów
    text.setFont(*font);
    text.setCharacterSize(240);
    text.setColor(Color::Black);
    info.setFont(*font);
    info.setColor(Color::Black);
    info.setCharacterSize(35);
    info.setString("Press ESC");
    info.setPosition(1280/2-info.getGlobalBounds().width/2,720/2-info.getGlobalBounds().height/2+150);

//ustawienie pozycji pól 
    for(int x=0; x<10; x++)
    {
        for(int y=0; y<10; y++)
        {
            pole[x][y].setPosition(x,y,100,100);
            pole_przec[x][y].setPosition(x,y,680,100);
            pole_przec[x][y].setIA(true);
        }
    }
}


Engine::~Engine()
{
}

void Engine::runEngine()
{
    bool menu=false;
    state=MARK;
    IAmark(); //zaznaczenie statków przez komputer



    while(!menu) //główna pętla aplikacji
    {
//ustawienie informacji o wyniku gry
        if(IApoints>19&&points>19)
        {
            state=OVER;
            text.setString("DRAW");

        }
        else if(IApoints>19)
        {
            state=OVER;
            text.setString("LOSE");
        }
        else if(points>19)
        {
            state=OVER;
            text.setString("WIN");
        }


        Event event;
        Vector2f mouse(Mouse::getPosition(*window)); //pobranie pozycji myszki

        while(window->pollEvent(event));
        {
            if (Keyboard::isKeyPressed(Keyboard::Escape))
//powrót do meny po naciśnięciu ESC
                menu=true; //powrót 

            if (event.type == sf::Event::Closed)
                window->close();

//sprawdzenie dla każdego pola przeciwnika czy jesteśmy na etapie zgadywania statków przeciwnika i czy kliknęliśmy dane pole
    for(int x=0; x<10; x++)
    {
        for(int y=0; y<10; y++)
                {
                    if(pole_przec[x][y].getBounds(mouse)&& state==SHOT && pole_przec[x][y].getShot()==false &&
                            (event.type==Event::MouseButtonReleased && event.key.code ==Mouse::Left ))
                    {
                        pole_przec[x][y].setShot(true)
                        if(pole_przec[x][y].getShip()==false||points>19) //jeśli nie trafiliśmy komputer zaznacza swój statek w przeciwnym razie komputer zgaduje 
                            IAshot();
                        else points++;



                    }
                }
            }

//sprawdzenie dla każdego pola gracza czy jesteśmy na etapie zaznaczania naszych statków i czy kliknęliśmy dane pole
          for(int x=0; x<10; x++)
            {
             for(int y=0; y<10; y++)
                {
                    if(pole[x][y].getBounds(mouse)&& state==MARK && pole[x][y].getShip()==false &&
                            (event.type == Event::MouseButtonReleased && event.key.code== Mouse::Left ))
                    {
                        pole[x][y].setShip(true);
                        mark++;
                        if(mark>=20)
                            state=SHOT;

                    }
                }
            }


        }
//zaktualizowanie danych i narysowanie planszy
        update();
        draw();
    }


}

void Engine::draw()
{

    window->clear(Color::White);
// jeśli gra się nie skończyła rysujemy plansze
    if(state!=OVER
    for(int x=0; x<10; x++)
    {
        for(int y=0; y<10; y++)
            {
                window->draw(pole[x][y]);
                window->draw(pole_przec[x][y]);
            }
        }
//jeśli gra się skończyła wyświetlamy informacje o wyniku gry
    if(state==OVER)
    {
        text.setPosition(1280/2-text.getGlobalBounds().width/2,720/2-text.getGlobalBounds().height/2-100);
        window->draw(text);
        window->draw(info);
    }


    window->display();
}

void Engine::update() //aktualizowanie informacji dla każdego pola
{
    for(int x=0; x<10; x++)
    {
        for(int y=0; y<10; y++)
        {
            pole[x][y].update();
            pole_przec[x][y].update();
        }
    }
}

void Engine::IAmark(
{
    int x,y;
    srand(time(NULL ));
//dwadzieścia razy losujemy pole na którym komputer zaznaczy swój statek jeśli pole było zaznaczone wcześniej losujemy drugi raz
    for(int i=0i<20i++)
    {
        do
        {
            x=rand()%10;
            y=rand()%10;
        }
        while(pole_przec[x][y].getShip()==true);

        pole_przec[x][y].setShip(true);
    }
}

void Engine::IAshot()
{
    int x,y;
    srand(time(NULL ));
//losujemy pole w które komputer będzie strzelał jeśli pole było zaznaczone wcześniej losujemy drugi raz
    do
    {
         x=rand()%10;
         y=rand()%10;
    }
    while(pole[x][y].getShot()==true);

    pole[x][y].setShot(true);
    if(pole[x][y].getShip()==true&&IApoints<20) //jeśli trafił wywołujemy funkcje jeszcze raz
    {
        IApoints++;
        IAshot();
    }
}

Teraz wystarczy w pliku game.h włączyć plik engine.h i skompilować i możemy podziwiać działającą grę.

Podsumowanie 

Dziś wreszcie udało nam się skończyć naszą aplikację okienkową. Kod całej aplikacji możecie znaleźć na moim profilu github. Jak zwykle zachęcam do zadawania pytań i zgłaszania propozycji w komentarzach.

czwartek, 3 listopada 2016

Menu gry

Witam 

Dziś zrobimy podstawowe menu dodajmy więc dwa pliki game.h i game.cpp odrazy otwórzmy ten pierwszy i zadeklarujmy wszystkie potrzebne zmienne i funkcje.

game.h

#include <SFML\Graphics.hpp>
#include <Windows.h>
#include <string>
#include "pole.h"

using namespace std;
using nam espace sf;
//dodanie potrzebnych bibliotek i zadeklarowanie przestrzeni nazw

class Game
{
public:
Game(void);
~Game(void)
//konstruktor i destruktor

void runGame(); //główna funkcja obsługująca grę

protected:
enum GameState {MENU,GAME,GAME_OVER,END}; //zmienna określająca status gry
GameState state

private:
Font font;

void menu(); //funkcja wyświetlająca grę
void single(); //funkcja wyświetlająca grę
};

Teraz zajmijmy się plikiem game.cpp 

#include "game.h"

RenderWindow window

Game::Game(void)
{
ContextSettings settings;
settings.antialiasingLevel = 8;
//stworzenie okna
window.create(VideoMode(1280,720),"Statki",Style::Default,
settings);

state = END;
window.setFramerateLimit(60);
//załadowanie czcionki
if(!font.loadFromFile("tahoma.ttf"))
{
MessageBox(NULL,"Font not found!","ERROR",NULL);
return;
}

state = MENU;
}


Game::~Game(void)
{
}


void Game::runGame()//sprawdza jaki jest status gry i wywołuje odpowiednią funkcję
{
while(state != END)
{
switch (state)
{
case MENU:
menu();
break;
case GAME:
single();
break;
}
}
}


void Game::menu()
{
//ustawienie i sformatowanie tytułu gry
Text title("STATKI",font,80);
title.setStyle(Text::Bold);
title.setColor(Color::Black);

title.setPosition(1280/2-title.getGlobalBounds().width/2,20);

const int ile = 2;
//ustawienie i sformatowanie opcji wyboru
Text tekst[ile];

string str[] = {"Play","Exit"};
for(int i=0;i<ile;i++)
{
tekst[i].setFont(font);
tekst[i].setCharacterSize(65);

tekst[i].setString(str[i]);
tekst[i].setPosition(1280/2-tekst[i].getGlobalBounds().width/2,250+i*120);
}

while(state == MENU)
{
Vector2f mouse(Mouse::getPosition(window));
Event event;

while(window.pollEvent(event))
{
//Wciśnięcie ESC lub przycisk X
if(event.type == Event::Closed || event.type == Event::KeyPressed &&
event.key.code == Keyboard::Escape)
state = END;

//kliknięcie MENU
else if(tekst[0].getGlobalBounds().contains(mouse&&
event.type == Event::MouseButtonReleased && event.key.code == Mouse::Left)
{
state = GAME;
}

//kliknięcie EXIT
else if(tekst[1].getGlobalBounds().contains(mouse&&
event.type == Event::MouseButtonReleased && event.key.code == Mouse::Left)
{
state = END;
}
}
//sprawdzenie czy gracz najechał na tekst i udpowiednie ustawienie koloru
for(int i=0;i<ile;i++)
if(tekst[i].getGlobalBounds().contains(mouse))
tekst[i].setColor(Color::Cyan);
else tekst[i].setColor(Color::Black);

//narysowanie wszystkich elementów
window.clear(Color::White);

window.draw(title);
for(int i=0;i<ile;i++)
window.draw(tekst[i]);

window.display();
}
}
// funkcja wywołująca właściwą grę
void Game::single()
{
Engine engine(window,font);

engine.runEngine();

state = MENU;
}

Na razie zawartość funkcji single dajmy sobie w komentarze ponieważ klasa engine nie została utworzona i program się nie wykona.


Teraz skompilujmy nasz program i podziwiajmy nasze menu

Podsumowanie

Dziś zrobiliśmy dużo, udało nam się stworzyć piękne menu. w następnej części zajmiemy się klasą engine. Jak zwykle cały kod aplikacji znajdziecie na moim profilu github. Jak zawsze zachęcam do zadawania pytań i komentowania.



poniedziałek, 31 października 2016

Kończymy klasę pole

Witam

Dzisiaj dalej zajmiemy się klasom pole. Od razu otwórzmy plik pole.cpp i wpiszmy poniższy kod

#include "pole.h"

using namespace sf;

Pole::Pole(int w,int h, int mX, int mY, bool sp,bool st,bool i)
{
//przypisanie podstawowych zmiennych
    width=w;
    height=h;
    marginX=mX;
    marginY=mY;
    ship=sp;
    shot=st;
    IA=i;

//rysowanie pustego pola
    shape.setSize(Vector2f(size_,size_));
    shape.setFillColor(Color(0,128,255));
    shape.setOutlineThickness(1);
    shape.setOutlineColor(Color::Black);
    shape.setPosition(marginX+width*(size_+1),marginY+height*(size_+1));
}

Pole::~Pole()
{
}
void Pole::setPosition(int x,int y,int mx,int my)
{
    //Zmiana pozycji pola
    width=x;
    height=y;
    marginX=mx;
    marginY=my;
    shape.setPosition(marginX+width*(size_+1),marginY+height*(size_+1));
}

bool Pole::getShip()
{
//zwracanie wartości ship
    return ship;
}

bool Pole::getShot()
{
//zwracanie wartości shoy
    return shot;
}

void Pole::update()
{
     if(shot&&ship) //sprawdzenie czy nie zestrzelono statku i wywołanie funkcji wyświetlających graficzny odpowiednik
       {
        drawShip();
        drawHit();
       }

    else if(ship==true&&IA==false) //sprawdzenie czy gracz nie zaznaczył na danym polu swojego statku i wyświetlenie odpowiedniej informacji
        drawShip();

    else if(shot==true) //sprawdzenie czy gracz nie spudłował naciskając dane pole i wyświetlenie informacji
        drawShot();
}

void Pole::draw(sf::RenderTarget &target, sf::RenderStates states)const //funkcja umożliwiająca rysowanie i transformowanie obiektu
{
    states.transform *getTransform();
//narysowanie wszystkich obiektów które chcemy narysować rysując obiekt naszej klasy
    target.draw(shape);
    target.draw(circle);
    target.draw(cross[0]);
    target.draw(cross[1]);
}

void Pole::drawShip() //funkcja odpowiada za narysowanie naszego statku
{

    circle.setRadius(size_/4);
    circle.setOrigin(size_/4,size_/4);
    circle.setPosition(marginX+size_/2+width*(size_+1),marginY+size_/2+height*(size_+1));
    circle.setOutlineThickness(size_/8);
    circle.setOutlineColor(Color::Red);
    circle.setFillColor(Color(0,128,255));

}

void Pole::drawShot() //funkcja odpowiada za narysowanie niecelnego strzału
{
    for(int i=0;i<2;i++)
    {
        cross[i].setSize(Vector2f(5,size_));
        cross[i].setOrigin(3,size_/2);
        cross[i].setFillColor(Color::Red);
        cross[i].setPosition(marginX+size_/2+width*(size_+1),marginY+size_/2+height*(size_+1));
    }
    cross[0].setRotation(45);
    cross[1].setRotation(135);
}

void Pole::drawHit() //funkcja odpowiada za narysowanie naszego trafienia
{
    circle.setFillColor(Color::Red);
}

void Pole::setShot(bool s) //funkcja ustawia wartość shot
{
    shot=s;
}

void Pole::setShip(bool s//funkcja ustawia wartość shot
{
    ship=s;
}

bool Pole::getBounds(Vector2f &m) //funkcja zwraca prawdę jeżeli mysz znajduje się w obrębie pola
{
    return shape.getGlobalBounds().contains(m);
}
void Pole::setIA(bool i//funkcja ustawia wartość IA
{
     IA=i;
}

Teraz w funkcji main stworzymy zwykłe okno z podstawową mechaniką

#include"pole.h"

int main()
{
    Pole p(0,0,20,20); //tworzymy obiekt klasy Pole
    p.setShip(1);
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window"); //tworzymy okno aplikacji
    while (window.isOpen())
    {
        p.update(); //aktualizujemy informacje na temat naszego obiektu
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) //naciśnięcie przycisku zamknij
                window.close();
        }

        window.clear(sf::Color::White)
        window.draw(p); //narysowanie naszego obiektu
        window.display();
    }

        return 1;
}

I wtedy naszym oczom ukarze się widok naszego pola

Możemy również pobawić się funkcjami setShip(), setShot() aby uzyskać takie efekty:
nasz statek

zbity statek

pudło
(dodatkowo zmieniłem rozmiar pola żeby było lepiej widać) 

Podsumowanie

Dziś wreszcie skończyliśmy klasę pole i zobaczyliśmy efekt naszej pracy jednak do ukończenia projektu jeszcze długa droga. Kod całej aplikacji możecie znaleźć na moim profilu github. Jak zwykle zachęcam do zadawania pytań i zgłaszania propozycji w komentarzach.

niedziela, 30 października 2016

Zaczynamy tworzyć aplikacje okienkową

Witam

Dzisiaj zaczniemy tworzyć nową grę. Jednak tym razem będzie to aplikacja okienkowa. Ponownie stworzymy  grę w statki jednak tym razem będzie ona wyglądać w ten sposób

Aplikacje pisałem przy użyciu biblioteki SFML 2.0, ale powinna ona działać również z późniejszymi wersjami dej biblioteki, ponieważ zachowuje ona kompatybilność wsteczną. Jeśli pierwszy raz słyszysz o tej bibliotece koniecznie odwiedź oficjalną stronę i zapoznaj się ze znajdującym tam tutorialem lub jeśli nie znasz angielskiego odwiedź blog Szymona Siarkiewicza. Oprócz znajomości tej biblioteki musisz również umieć korzystać z obiektowego C++, możesz się go nauczyć z kursu Mirosława Zelenta

W naszym projekcie stworzymy trzy klasy, teraz pokrótce opisze do czego służy każda klasa

pole.h-ta klasa będzie odpowiadać za narysowanie każdego pola i wyświetlenie czy statek na tym polu został trafiony czy nie czy gracz porostu spudłował.

engine.h-ta klasa odpowiada za główna mechanikę gry, będzie sprawdzać czy gracz nie wybrał któregoś pola i będzie wywoływać odpowiednie funkcje klasy pole.

game.h-jest to główna klasa całej aplikacji odpowiada ona za renderowanie okna i początkowe menu.

Teraz stworzymy klasę pole, dzisiaj zajmiemy się tylko plikiem nagłówkowym. 


#include <SFML/Graphics.hpp>
#pragma once

class Pole: public sf::Drawable,
sf::Transformable
{

    bool ship//wartość prawda jeżeli na polu znajduje się statek
    bool shot//wartość prawda jeżeli ple było zaznaczone
    int height, width, marginX, marginY, size_=48;//?
    bool IA//wartość prawda jeżeli pole jest komputera, jest ona potrzebna ponieważ będziemy używać tej samej klasy do rysowania naszych pul i pul przeciwnika

    public:

    Pole(int=0,int=0,int=1,int=1,bool=0,bool=0,bool=0)
    ~Pole();
//konstruktor i destruktor



    void setPosition(int,int,int,int)//ustawia pozycje pola

    bool getShip();//zwraca prawdę jeśli na polu jest statek
    bool getShot();//zwraca prawdę jeśli było trafienie
    bool getBounds(sf::Vector2f &m)//zwraca prawdę jeśli myszka najedzie na obiekt

    void setShot(bool)//zmienia wartość zmiennej shot
    void setShip(bool)//zmienia wartość zmiennej ship
    void setIA(bool)//zmienia wartość zmiennej IA

    void update(); //aktualizuje informacje o polu
    void drawShot(); //rysuje strzał który był nietrafiony
    void drawShip(); //rysuje statek
    void drawHit(); //rysuje celny strzał

    sf::RectangleShape shape//puste pole
    sf::CircleShape circle; //statek
    sf::RectangleShape cross[2]//krzyżyk symbolizujący pudło


    virtual void draw(sf::RenderTarget &target,sf::RenderStates states) const; //funkcja umożliwiająca transformowanie obiektów klasy pole
};

Chciałbym teraz dokładniej opisać linijkę którą oznaczyłem "//?":

 width i height-oznaczają współrzędne danego pola względem całej planszy gracza licząc od lewego górnego rogu.

marginX i marginY-oznaczają jaką odległość ma mieć lewy górny róg planszy od lewego górnego narożnika okna aplikacji.

size-oznacza porostu wielkość jednego pola.

Podsumowanie

Na dziś już starczy tego pisania. Zachęcam do samodzielnego przeanalizowania kodu znajdującego się na moim profilu github. Jak zawsze zachęcam do komętowania.