Arkanoid w SFML – sprawdzenie kodu i pomoc w organizacji

0

Cześć. Nadmienię że nigdy nie pisałem na żadnym forum, zawsze starałem się sam znaleźć rozwiązanie w necie, doszedłem do etapu, że bardzo potrzebuję pomocy i rady doświadczonych programistów.

Otóż staram się napisać grę na wzór Arkanoida, samą grę już mam, niestety napotykam wiele problemów aby ją poskładać do kupy.
Stworzyłem maszynę stanów (StateMachine) obsługującą zdarzenia SFML, nie potrafię jednak za pomocą metody window.draw() wyrysować obiektów stworzonych w poszczególnym stanie na ekran. Próbowałem przenosić pętlę główną do klasy StateMachine, jednak to nic nie dało. (W przyszłości mam zamiar użyć tablicy obiektów za pomocą unique_ptr, jak tylko ogarnę tworzenie za pomocą new i delete)

Kod, który zamieszczam służy mi do testowania implementowanych rozwiązań.

Będę bardzo wdzięczny za wszelką pomoc.

1

Cześć, a więc:

maszynę stanów (StateMachine) obsługującą zdarzenia SFML

Gdzie ona jest? W którym pliku? Może pokaż fragment kodu tej klasy i jej wykorzystanie.

nie potrafię jednak za pomocą metody window.draw() wyrysować obiektów

Dlaczego? Otrzymujesz jakiś błąd? Jaki? Nie otrzymujesz błędu, ale po prostu nie wiesz jak powinien wyglądać kod, który narysuje te obiekty? Po drugie jakie obiekty? Twoje? Domyślne obiekty SFML'a?

Kod, który zamieszczam służy mi do testowania implementowanych rozwiązań.

Czyli nie musimy go pobierać jeśli nie interesuje nas sposób w jaki testujesz swoją grę. Ale co z właściwym kodem gry, który stwarza problemy?

Podsumowując: Wiem, że

Nadmienię że nigdy nie pisałem na żadnym forum

ale musisz ułatwić nam pomaganie, jeśli takiego oczekujesz ;)

0

Bardzo dziękuję za odpowiedź.
Mam problem jak poskładać wszystko do kupy, żeby było jednym "organizmem".

main.cpp

Tutaj tworzę obiekt okna, ustawiam parametry, tworzę główną pętlę "gry", obsługującą zdarzenia (wrzucając je do zmiennej event). Tutaj wywołuję instancję singletonu State machine, następnie w pętli wywołuję jej metodę handleInput, posyłając event.

#include <SFML\Window.hpp>
#include <SFML\Graphics.hpp>

#include "StateMachine.h"

using namespace sf;


int main()
{
	CircleShape cir;
	StateMachine* machine = StateMachine::Instance();

	RenderWindow window(VideoMode(800, 600), "State machine basic");
	window.setFramerateLimit(30);

	while (window.isOpen())
	{
		Event event;

		while (window.pollEvent(event))
		{
			switch (event.type)
			{
			case Event::Closed:
			{
				window.close();
				break;
			}
			}

			machine->handleInput(event);
		}


		// clear
		window.clear(Color::Blue);

		// draw
		//window.draw(machine->_state); - tutaj bym rysował stworzoną instancję obiektu?

		// display
		window.display();
	}

	return EXIT_SUCCESS;
}

StateMachine

tutaj mam wskaźnik polimorficzny typu State, która posiada wirtualne metody, z których dziedziczą klasy Menu i Game, State przyjmuje stan obiektu potomka (początkowo ustawiony na Menu). Tutaj podaję dalej w handleInput, otrzymany event, do stanu _state->handleInput(), czyli do Menu.

.h

#pragma once
#include <SFML\Graphics.hpp>
#include <iostream>

#include "State.h"
#include "Menu.h"
#include "Game.h"

class StateMachine
{
public:
	static StateMachine* Instance();

	void handleInput(Event);
	void changeState(State*);

protected:
	StateMachine();
	~StateMachine()=default;

private:
	static StateMachine* _instance;
	State * _state;

};

.cpp

#include "StateMachine.h"



StateMachine* StateMachine::_instance = 0;

StateMachine* StateMachine::Instance()
{
	if (_instance == 0)
	{
		_instance = new StateMachine;
	}
	return _instance;
}


StateMachine::StateMachine()
	:_state(Menu::Instance())
{
}


void StateMachine::handleInput(Event event)
{
	_state->handleInput(event);
}



void StateMachine::changeState(State* newState)
{
	_state = newState;
}

State

wirtualne metody odziedziczane przez Menu i Game:

.h

#pragma once
#include <SFML\Graphics.hpp>
#include <iostream>

using namespace sf;

class State: public Drawable
{
public:
	State() {}
	~State() {}

	virtual void handleInput(Event) {}
	virtual void draw(RenderTarget &target, RenderStates states) const {}
};

Potomkowie, tutaj się pojawia mój problem. Dziedziczą z Klasy State, jak również z klasy Drawable. Nadpisałem metodę draw, jednak gdybym ją chciał wywołać nie udaje mi się posłać tam obiektu window, ponieważ nie jest zgodny z typem, przyjmowanych parametrów przez draw. Próbowałem również dostać się do instancji _state z StaeMachine (tak jak jest to pokazane w kodzie), jednak to też nie działa. Chodzi mi tylko o użycie tej metody draw z klasy Menu albo Game.

Menu

.h

#pragma once
#include "State.h"
class Menu :public State
{
public:
	static State* Instance();
	void handleInput(Event);
protected:
	Menu()=default;
	~Menu()=default;
private:
	 static State* _instance;

	 void play();
	 void set();
	 virtual void draw(RenderTarget &target, RenderStates states) const override;
	 CircleShape circle;
	 Font font;
	 Text text, text2;
	 Vector2f screen{ 400,300 };
	 float radiusBall{ 15.f };
	 float vocity{ 8.0f };
	 Vector2f vocityBall{ 4.f,vocity };
};

.cpp

#include "Menu.h"
#include "StateMachine.h"

State* Menu::_instance = 0;

State* Menu::Instance()
{
	if (_instance == 0)
	{
		_instance = new Menu;
	}

	return _instance;
}

void Menu::handleInput(Event event)
{
		std::cout << "Menu::handleInput" << std::endl;
		RenderStates* st{0};
		this->set();
		this->play();
		/*
		if (event.key.code == Keyboard::Q)
		{
			StateMachine* state = StateMachine::Instance();
			State* game = Game::Instance();
			state->changeState(game);
		}*/
}

////////////////////////////////////////////////////////


void Menu::set()
{
	if (!font.loadFromFile("arial.ttf"))
	{
		std::cout << "Font load error" << std::endl;
	}

	text.setFont(font);
	text.setString("THE ARKANOID GAME");
	text.setFillColor(Color::Cyan);
	text.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2);
	text.setPosition(screen);
	text.setScale({ 1.5f,1.5f });


	text2.setFont(font);
	text2.setString("Made by: Ciepano");
	text2.setFillColor(Color::White);
	text2.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2);
	text2.setPosition({ 430,570 });

	circle.setFillColor(Color::Red);
	circle.setRadius(radiusBall);
	circle.setOrigin(radiusBall, radiusBall);
	circle.setPosition({ 100,100 });
}


void Menu::play()
{
	circle.move(vocityBall);

	if (circle.getPosition().y + circle.getRadius() >= 600)
	{
		vocityBall.y = -vocity;

	}

	else if (circle.getPosition().y - circle.getRadius() <= 0)
	{
		vocityBall.y = vocity;

		text.setFillColor(Color::Green);
	}

	if (circle.getPosition().x > 850 || Mouse::isButtonPressed(Mouse::Left))
	{
		// ctrl+v
		StateMachine* state = StateMachine::Instance();
		State* game = Game::Instance();
		state->changeState(game);
	}
}


void Menu::draw(RenderTarget &target, RenderStates states) const
{
	target.draw(text);
	target.draw(text2);
	target.draw(circle);
}

Zakładam że mogę popełniać mnóstwo błędów, ponieważ jestem samoukiem, aczkolwiek staram się nieustannie z tym mierzyć i rozwijać swoją wiedzę.

Każda informacja będzie dla mnie bardzo cenna merytorycznie.

1

1. Z tego co widzę, wszystkie Twoje metody draw przyjmują dwa argumenty, jednak Ty próbujesz wrzucić tam jeden:

window.draw(machine->_state);

2. To co wrzucasz do swojej implementacji metody draw, to obiekt typu State będący obiektem typu Drawable, którego według tego co mówi dokumentacja nijak ma się do typu jakim jest okno, w którym można rysować, czyli RenderTarget.

3. Moim zdaniem trochę się pogubiłeś, zobacz:
Masz obiekt, który jest Twoim Menu i chcesz to Menu wyświetlić, ok. Więc tworzysz metodę, która je narysuje na określonym oknie, Twoim target - To też jest ok.
Problem robi się, gdy zapominasz jaki miałeś zamysł i próbujesz wywołać swoją metodę draw podając jej maszynę stanów, a nie okno, w którym chcesz rysować.

4. Dodatkowo, zadeklarowałeś metodę przyjmującą obiekt, z którym nic nie robisz:

void Menu::draw(RenderTarget &target, RenderStates states) const
{
    target.draw(text);
    target.draw(text2);
    target.draw(circle);

    // A gdzie wykorzystanie obiektu  states  ?
}

5. Robisz trochę błędów, ale idzie Ci dobrze, kod jest w miarę czytelny (jest parę nieścisłości, na przykład tutaj: state->changeState(game);, albo tutaj: text.setPosition(screen);), a błędy popełnia każdy.
Mam tylko jedną prośbę - Nie rób przerw między sekcjami if... else if... else, tak jak zrobiłeś tutaj:

    if (circle.getPosition().y + circle.getRadius() >= 600)
    {
        vocityBall.y = -vocity;
 
    }
 
    else if (circle.getPosition().y - circle.getRadius() <= 0)
    {
        vocityBall.y = vocity;
 
        text.setFillColor(Color::Green);
    }

utrudnia to spojrzenie na całość.

Jak coś jeszcze znajdę, to dopiszę jako edit.

1 użytkowników online, w tym zalogowanych: 0, gości: 1