I'm making a 2D grid based game in Qt.
When clicking on a square in a grid the player moves to that square following a path calculated with an A* algoritm. However I would like to be able to animate this. So instead of immediately going to the goal the player has to go from square (node) to square until it reaches the goal at a speed that the user can set.
Question: What is the easiest way to achieve this?
Personally, I'd design this similar to the following:
class Player : public QObject {
...
QPoint pos;
QList<QPoint> path;
QPropertyAnimation posAnimation;
};
Define pos as a Q_PROPERTY. This enables you to use QPropertyAnimation to define an animation on this value for animating the movement between two adjacent points. After the animation is done, take() one point from the path and reconfigure the animation, giving you an animation along the whole path.
Use a slot animationFinished() in the Player class to provide the next point to the animation.
To start such an animation, fill the path with the values (in a function move(QList<QPoint> path) or similar), set the values of the animation and start it.
These code snippets should help you:
// in constructor:
posAnimation.setPropertyName("pos");
posAnimation.setTargetObject(this);
connect(&posAnimation, SIGNAL(finished()), SLOT(animationFinished()));
// in the slot:
if(!path.empty()) {
posAnimation.setStartValue(pos());
posAnimation.setEndValue(path.takeFirst());
posAnimation.start();
}
To define pos as a property, you have to define two slots: A reading and a writing function, also known as a getter and a setter:
class Player : public QObject {
Q_OBJECT
Q_PROPERTY(QPoint pos READ pos WRITE setPos) // define meta-property "pos"
...
public slots:
QPoint pos() const; // getter
void setPos(QPoint p); // setter
private:
QPoint m_pos; // private member
};
QPoint Player::pos() const {
return m_pos;
}
void Player::setPos(QPoint pos) {
m_pos = pos;
}
The Q_PROPERTY line just declares a meta-property. This has nothing to do with C++, but Qt's meta object compiler parses this line and adds an entry to the internal property list. Then, you can say player->property("pos") to access the position instead of player->pos(). You may wonder why this is useful. It's useful whenever you only want to pass around a property name as a string, like to tell the QPropertyAnimation which property to animate. Another scenario is when using scripting like QML. Then you define properties all over your classes. You can read more about meta-properties in the Qt documentation: The Property System.
Take a look at the demo of the tower defense game Squaby made with the V-Play (v-play.net) engine. You can access the full source code here: Squaby.
V-Play provides you with game components for path finding and more (API reference).
Related
I first have to say that I'm new to Unreal :/
What I would like is two classes :
One would be a component (CubeBlockComponent) which would be a CollisionBox with a Mesh, so I would like to use a UBoxComponent and a UStaticMeshComponent.
Second would be an Actor (ModuleActor) combining several CubeBlockComponent.
I was able to get the desired behavior with an actor composed of several BoxComponent and StaticMeshComponent, but configurating them the same way each time I want to add one block is a pain, thus the need of a CubeBlockComponent.
My problem is I can't figure out how to do it properly. What should I do ?
I tried creating a c++ class (CubeBlockComponent) inherited from USceneComponent. I added a UStaticMeshComponent and a UBoxComponent :
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UStaticMeshComponent* cubeStaticMesh;
UPROPERTY(EditAnywhere)
UBoxComponent* boxCollision;
And created them with CreateDefaultSubobject in the constructor:
// Create box collision
boxCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxCollision"));
boxCollision->SetBoxExtent(FVector(40.f, 40.f, 40.f));
boxCollision->SetupAttachment(this);
// Create Cube static mesh
cubeStaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeStaticMesh"));
cubeStaticMesh->SetupAttachment(boxCollision);
Then, back in UE5 editor, I can make a blueprint component (BpCubeBlockComponent) based on my class (CubeBlockComponent).
But this is bad as in BpCubeBlockComponent I can only modify cubeStaticMesh and boxCollision via "Open selecttion in Propery Matrix". And also, when I combine several BpCubeBlockComponent in an actor, the meshes just stays at the origin.
I feel like I should just create a class inheriting UBoxComponent, and basically recreating all UStaticMeshComponent functionalities inside the class, which would be a pain...
After hours of testing I found a solutions which I found quite good.
First, one should know that adding a component from another component is not recommended ; I guess it does not go well with Actor/component principle.
That said I was able to do it by overloading the OnRegister() method. So once a component is registered, it can attach other component to itself.
This is how it looks :
// CubeBlockComponent.h
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent), Blueprintable )
class BASIC2D_API UCubeBlockComponent : public UBoxComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UCubeBlockComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
// Needed to attach other component
virtual void OnRegister() override;
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UStaticMeshComponent* cubeStaticMesh;
};
And
CubeBlockComponent.cpp
#include "CubeBlockComponent.h"
// Sets default values for this component's properties
UCubeBlockComponent::UCubeBlockComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// Create Cube static mesh, wait OnRegister to attach
cubeStaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeStaticMesh"));
}
// Called when the game starts
void UCubeBlockComponent::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void UCubeBlockComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
void UCubeBlockComponent::OnRegister()
{
Super::OnRegister();
cubeStaticMesh->SetupAttachment(this);
}
I am having a QGraphicsView which contains some QGraphicsItem I have a feature (Hide Item) which on mouse right click, hide desired QGraphicsItem(Rectangle) and its connected polylines. I have a Undo-Redo feature also.
Undo - It should cancel the effect of last command executed and show
previous transformation.
Redo - It will undo the previous Undo.
To implement this Undo-Redo feature I have used Command pattern. I have implemented Undo-Redo feature for ZoomIn-ZoomOut.
Question is : I dont know how to implement Undo-Redo for Hide feature. Means what to push into stack, what to pull ?
Below Undo-Redo code is for ZoomIn-ZoomOut feature. (It is just for reference that I want to implement Hide feature something like this. )
myCommand.c
class myCommand: public QUndoCommand
{
public:
myCommand();
myCommand(double scale, QGraphicsScene* scene,QGraphicsView* view);
private:
QGraphicsItem* mItem;
QGraphicsScene* mScene;
QGraphicsView* mView;
double scaleFactor;
void undo();
void redo();
}
myCommand.cpp
myCommand::myCommand(double scale, QGraphicsScene *scene,QGraphicsView* view): mScene(scene),
mView(view),scaleFactor(scale)
{}
void guiCommand::undo()
{
mView->scale(1/scaleFactor,1/scaleFactor);
}
void myCommand::redo()
{
mView->scale(scaleFactor,scaleFactor);
}
myView.cpp
void myView::ZoomIn()
{
double scaleFactor = 1.1;
view->scale(scaleFactor,scaleFactor);
myCommand* command1 = new myCommand(scaleFactor,scene,view);
undoStack->push(command1);
}
myView.h
public:
QUndoStack* undoStack;
New addition :
void myRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
vPtr = this->getPtr();
if(vPtr->isVisible == false)
this->hide();
else
{
this->show();
qDebug()<<"Undo Rect";
}
}
myCommand is :
myCommand* command3 = new myCommand(isRectHiddden,vPtr,GraphName);
undoStack->push(command3);
It should be very simple given the fact that you already successfully implemented zoom-in/zoom-out.
class HideItemCommand: public QUndoCommand
{
public:
explicit HideItemCommand(QGraphicsItem *item);
private:
QGraphicsItem* mItem;
void undo();
void redo();
}
HideItemCommand::HideItemCommand(QGraphicsItem *item): mItem(item)
{}
void HideItemCommand::undo()
{
mItem->show();
}
void myCommand::redo()
{
mItem->hide();
}
void myView::hideItem(QGraphicsItem* item)
{
item->hide();
auto cmd = new HideItemCommand(item);
undoStack->push(cmd);
}
So this is very simple. BUT!!! Now I am going to think one or two steps ahead. You asked only about showing/hiding undo/redo which can be implemented using the code which I suggested. But you are probably developing some drawing app so I guess that sooner or later you will want also add-item or remove-item undoable/redoable commands. And then the code which I wrote will not suffice any more. The reason is that holding item by pointer will not work any more if you remove and then add again the item using undo/redo. After redoing of remove operation, the pointer to the newly re-created object will be different the the pointer to the corresponding object which you had deleted earlier, so the item kept in HideCommand via its pointer will be invalid.
To solve this problem of invalid pointers, you need to invent some other way of recording what items you have in your scene. For example some UUID or just a sequence of integers (which is what I would do), lets call it item ID. And then keep a two-way map of these item IDs and corresponding pointers, so that you are able to translate ID to the item pointer, there and back.
Then when you create an item via some AddItemCommand you create the item and generate its ID and store the relation between the ID and the pointer to the map. You put record of this ID in the undo command. And for all other commands which will need to refer to that item (e.g. that HideCommand) you will use the ID instead of the pointer. This will allow you put all commands to the stack, use stable IDs and not volatile pointers which may change as you undo/redo adding or deleting of objects. Also RemoveItemCommand will record the ID of the removed object and if undone, the new item will be created (i.e. a new, different pointer) but with the old, known ID. So other commands referencing this ID will still be valid.
I hope I managed to explain this well. In fact it is not that difficult. You just need to understand that pointers will change over time if you add or remove items with undo/redo, but IDs can stay the same. Therefor you need to keep IDs in your commands and not pointers. This will of course change the code which I wrote a bit. But I believe you are able to adjust it from using pointers to using IDs by yourself once you implement the ID <-> pointer mapping in your app.
And again, trying to implement bezier curves redactor. There is
class BezierNode : public QGraphicsItem
BezierNode::BezierNode(QPointF point, Type type) : QGraphicsItem()
{
setPos(point);
setFlags(ItemIsMovable | ItemSendsScenePositionChanges | ItemSendsGeometryChanges);
}
It properly moves around in scene on mousePress + mouseMove, and I can catch events in itemChange() for some additional acting with control points. In QGraphicsItem::mouseMoveEvent() (according to Qt source on gitorious) there is a call to item->setPos(...). However, if I try to reimplement BezierNode::setPos(..), it's never triggered on moving object.
void BezierNode::setPos(const QPointF &pos) {
qDebug() << "setPos " << pos;
m_point = pos;
QGraphicsItem::setPos(pos);
}
In my case setPos() triggers only in constructor (there I call it manually). Yes, it moves in scene properly, I can get its position with pos() and use it everywhere instead of m_point, but I want to understand, what happens there.
Thanks in advance.
QGraphicsItem::setPos() is not virtual, so you can't override it. That's why BezierNode::setPos() will never be called.
This is the structure I have. I want to find the total size of label_2, or the QVBoxLayout, as it's displayed. When I use verticalLayout_2->width(), I always get 100 and verticalLayout_2->height() always returns 30. It's set to expanding, so I thought it would fill the area, which is 385x379, according to Qt Creator.
Doing label_2-width() and label_2-height() also results in 100x30, regardless of the window size and the area I thought it would expand to.
There is nothing much visually going on after your widget's constructor has been run. The setupUi call happens in the constructor. The real work happens once the event loop gets going.
Your real problem is that you should not be checking the size at an arbitrary point in time. You should be checking it each time it changes. To do this, you need your own layout. All it takes is to derive from an existing layout, and reimplement setGeometry. This method is called each time the parent widget or parent layout resizes the given layout. That is the only correct approach, and it doesn't require any hacks to accomplish.
For example, the following class could be used to signal when there's a new geometry:
class SigBoxLayout : public QBoxLayout {
Q_OBJECT
protected:
void setGeometry(const QRect & r) Q_DECL_OVERRIDE {
if (r != geometry()) emit hasNewGeometry(r);
QBoxLayout::setGeometry(r);
}
public:
SigBoxLayout(QBoxLayout::Direction dir, QWidget * parent = 0) :
QBoxLayout(dir, parent) {}
Q_SIGNAL void hasNewGeometry(const QRect & r);
};
In most or all object oriented games, each class relies on not just its own but parent classes. How is this class connection implemented in C++? Do you just add a pointer for the parent class you need or is there a better way?
For example a football game, when the person class clicks it ask the scene class if he is kicking any balls, and if he is then move it. I hope this is understandable and not too abstract.
I don't think passing along the parent in a constructor is a good idea. Instead, you should be using a class that maintains a list of all game elements and facilities interactions between them; for instance, the Game class illustrated below would check for collisions between any two players and if it's detected tell each of them that they were hit and by who.
I'm not sure if this will benefit you at all, but I typed it up for my initial response so I might as well submit. Note that all of this is still relevant if you're talking about a text-only game, just ignore the allusions to graphics in that case. Game design is based around a continuous game loop, and can be thought of very simply as:
while(true)
for each tick:
react to user input
update player, enemies, objects, etc.
end while
Where "tick" is every iteration of the game clock, however you choose to implement it--based on the fps, each second, whatever. In your example, the user clicks the football, the game sees the click and tells the football to move. To do this very simply, maintain a list all of all of the game elements within a class that maintains the state. To illustrate, here are is a very basic way you could implement this:
class Game {
vector<GameElement> elements;
Football football;
Player currentPlayer;
Game() {
this.football = new Football();
}
void update() {
for e in elements:
e.update();
// Once every element has been updated for the current tick, redraw them on the screen
screen.redraw();
}
void addElement(GameElement e) {
elements.add(e);
}
}
class GameElement {
int posx, posy; // screen coordinates
void paint() {}; // method to draw this element to the screen
virtual void update();
}
class Football: public GameElement {
bool kicked;
int anglex, angley;
double velocity;
void update() {
if(kicked){
// update position, angle, velocity; slow it down, check for bounce, whatever
posx = new x position;
posy = new y position;
if(velocity == 0)
kicked = false;
}
paint(); // call the paint method after the new position has been found
}
}
Assume you have another class that inherits from GameElement, Player, with a method kick(football) that sets the passed football into motion--i.e., sets kicked=True. So to initialize the game, you'd set it up with something like:
Game game = Game();
game.add(new Football());
game.add(new Player());
while(true) {
if(user_input==X)
game.currentPlayer.kick(football);
game.update();
sleep(1);
}
This could be changed to maintain, for example, layers instead of the entire game, then a higher level class could call update of each layer in order, allowing things to be painted over each other and children to only interact with siblings. There are a lot of possibilities.
Ran into similar questions working on a poker game: Here's the way I did it:
In your example, add a scene * the_scene to the constructor of person. Then, when person is initialized pass it a pointer to scene. Since you said parent and child, if the parent Is scene then it would just use "this" and it would send the address of the parent.
Then again, it seems like that's how you were going to do it anyway. One more thing, if you need person to interact with more than one class that is not directly inside it you can make some kind of container class that would store the pointers to all of them and just pass person that one to avoid having a constructor with too many parameters.