Drawing a minimap in SFML/C++ - c++

I understand that I can already create a minimap by modifying a view object, as outlined here, but what I want instead is a small rectangle that will have a box or an arrow representing the player, and other colored boxes that represent enemies or various objects. Is there a better way to do this than simply create a Minimap object that calculates where each enemy is and translates that into a representational object (box, arrow.. etc) that then gets drawn to the screen?
This is a general question more than anything, so I'm not really expecting sample code, even though it would be much appreciated. I'm outlining a minimap class now and I'll upload it here soon.
Here is what I'm doing in my draw loop:
minimap.setCenter(player.getPosition());
minimap.setViewport(sf::FloatRect(0.75f, 0.75f, 0.25f, 0.25f));
//Bottom right corner of screen
window.setView(minimap);
window.draw(player);
window.setView(view); //Return to default view
With a call to minimap.zoom(10); when the view is initiated.

Actually, I tried this:
window.setView(minimap);
sf::CircleShape object;
object.setRadius(200);
object.setFillColor(sf::Color::Blue);
object.setPosition(player.getPosition());
window.draw(object);
object.setFillColor(sf::Color::Red);
for (auto& e : enemies)
{
object.setPosition(e->getPosition());
window.draw(object);
}
window.setView(view);
And that worked. Obviously it would benefit from being in its own class, so that the circle didn't have to be recreated each loop etc..
If there's a better way to do this, be sure to mention it.
Here's a screenshot of the Minimap (Bottom Right):

Related

SFML View: setCenter vs move. It's driving me crazy

I come again to ask for help, based on the post SFML Views setCenter vs rotation.
It was to be something absurdly simple, but it's becoming a nightmare!
What I want is simple: move a view to the upper left corner of the global window and rotate it, but the axis should be at 0,0 and not at the center of the view, like the default.
The problem is that "setCenter" moves the view and "move" changes the center of the view. That is, one cancels the other.
I read a lot, I asked a lot, but so far no one gave me the solution.
If you look at the documentation of the sf::View class (https://www.sfml-dev.org/tutorials/2.0/graphics-view.php):
"Unlike drawable entites such as sprites or shapes, whose positions
are defined by their top-left corner (and can be changed to any other
point), views are always manipulated by their center -- this is
simply more convenient. That's why the function to change the
position of a view is named setCenter, and not setPosition."
It states that a view's origin is at its center. So if you want to fit the view to the upper left corner just do it with:
view.setCenter(view.getWidth() / 2, view.getHeight() / 2);
If you would like to center the corner of the window in the center of the view just do:
view.setCenter(0,0);
Now if you rotate the view, it always rotates around its center, so position the view appropriately before rotating it.
Let me know if you have further questions and if it helped.
Does the following work for you?
// This should set the view to the upper left corner with a size of 500x300
view.reset(sf::FloatRect(250, 150, 500, 300));
// Now you can rotate the view around its center:
view.setRotation(90);
// Don't forget to apply the view.
window.setView(view);
Hope this helps.
Fortunately I received support from a SFML forum user and the solution is in post https://stackoverflow.com/a/50069393/5074998.

How to set the visible area of a sf::Text

I am making some classes to help create a GUI using SFML. I would like to be able to display only a part of a sf::Text. Something like setTextureRect in the sf::Sprite class.
The only solution I found was to draw the text to a sf::RenderTexture and then I assign its texture to a sprite. I can then draw this sprite on the window.
//The class contains :
sf::RenderTexture buffer;
//When the class is created :
buffer.create(window.getSize().x,window.getSize().y);
//To draw on the screen :
window.clear();
buffer.clear();
buffer.draw(text);
buffer.display();
sprite.setTexture(buffer.getTexture());
window.draw(sprite);
window.display();
This allows me to use sf::Sprite::setTextureRect, but it is very slow. I measured that it is about 100 times slower than a direct draw. I tried creating the buffer to the size of the text but it had almost no impact on the performance.
So my question is, is there a way to display only a part of a sf::Text (or sf::Shape) that would be more efficient?
You can use sf::View to determine what is drawn and what not.
Remember that the rectangle that is drawn, with respect to the window, use the range 0.f - 1.f.
The tutorials can help.
As you saw, there's no method to do this in sf::Text.
http://www.sfml-dev.org/documentation/2.3.1-fr/classsf_1_1Text.php
However, if as you said, you want to do this: http://i.imgur.com/FsvnP.png
You can compute the size of the sf::Text and cut it before it exceeds the size of the frame (and eventually replace the last characters).
For example, "Person 2554548747848874874" will be "Person 25545..".
It's smooth and a lot of list are done this way. (You have an example with the Windows task explorer)
As DarkPhantom said, there's also sf::View, but you'll have to try to see if it's reliable to do a lot of setView().

How can I get a child CCSprite to draw on top of its parent?

I'm trying to control the order in which CCSprites are being layered one on top of the other. I'd use zOrder, but I'm animating characters made up of articulated sprites (some sprites parented on others with addChild) and zOrder is only honored among sibling sprites.
Basically, I want to parent my sprites so they can inherit each others' transforms, but I want to determine the draw order.
Looking around, it sounds like using a CCSprite's vertexZ property is the way to go. I've tried that. I set the draw method of my custom CCSprite like so:
- (void)draw
{
glEnable(GL_BLEND);
CHECK_GL_ERROR_DEBUG();
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
CHECK_GL_ERROR_DEBUG();
[super draw];
glDisable(GL_BLEND);
}
Each of my sprites now has a zOrder of 0 and a unique vertexZ to layer them as I'd like, but it's not working: any sprite that has a parent is not displayed!
Trying some things, I find if I don't set vertexZ, all sprites are displayed. And if I keep the vertexZs and don't parent, they're all displayed. But with vertexZs set, any child sprite won't display.
What's going on here and how can I get it to work?
Update: More clues. I can comment out the GL functions around my call to [super draw] entirely and the results are the same. Also, it turns out child sprites with a vertexZ do display, but wherever they overlap any other sprite they go invisible. The purple paper sprite is a child of the creature's left forearm sprite (on screen right) yet should go on top of it.
For the picture above, I'm basically doing this in my CCLayer. I want the parenting, but I want hand and paper to be on the top.
[self addChild:lUpperarm];
[self addChild:lForearm];
[self addChild:face];
[self addChild:rUpperarm];
[self addChild:rForearm];
[lForearm addChild:handAndPaper];
lUpperarm.vertexZ = -100;
lForearm.vertexZ = -99;
face.vertexZ = -98;
rUpperarm.vertexZ = -97;
rForearm.vertexZ = -96;
handAndPaper.vertexZ = -95;
This is how I want it to look. (I changed the last line to [self addChild:handAndPaper] which loses lForearm's transformations, not what I want.)
Update 2: Some have suggested I add all children with zOrder set to -1: e.g. [lForearm addChild:handAndPaper z:-1]. This changed things a little, but still didn't fix it, alas. Seems either vertexZ is not determining draw order and/or the blending is wrong.
I dealt with this issue when writing an RPG with Cocos2d. The problem is the alpha blending. As you are seeing, the paper is being covered up by the left arm sprite, even though it is transparent. For some reason, when Cocos2d's vertexZ technique is used, this problem can sometimes occur. To fix it, you can try changing your alpha blending level. Try 0.0, 0.5 and 1.0:
-(void) draw
{
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
[super draw];
glDisable(GL_ALPHA_TEST);
}
If that doesn't work, then I recommend a complete workaround solution. Keep all your sprites as part of the same parent node and use regular z ordering. It's not an ideal solution, but if you use a "meta" parent node then you can still transform all the children easily.
While adding only u can control z-order?
[self addChild:lUpperarm z:1];
[self addChild:lForearm z:2];
[self addChild:face z:3];
[self addChild:rUpperarm z:4];
[self addChild:rForearm z:5];
[lForearm addChild:handAndPaper z:6];
I can't be sure that this is your problem but I've had issues in the past when playing with draw() and it turned out to be that I wasn't resetting the blendfunc at the end of the call.
Here's a draw function I use that does some basic drawing with some transparency. Again, no guarantees.
- (void) draw {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
ccDrawSolidPoly(vertices, 4, fillColor);
// This is the important bit. Must do this last.
//
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
}
set higher z value to set object on top. and lower z value to set in back..

screen size in cocos2d

i would like to change the screen size so that sprites will disappear before they reach the real screen edges.
BUT i still want my background to stay on all of the screen size.
Imagine a paper on my screen so i want to game to exist only on that paper, and around that paper there still will be some background.
so, how do i set my CCSprites to move in and out from that paper and slowly disappear when coming to its edges ?
my sprites are moves with : (i need to put some code to get published cause site "standard" )
id moveclouds1 = [CCMoveTo actionWithDuration:30 position:ccp(420,380)];
thanks.
you can use glscissor for that
simply subclass a CCLayer to make your "paper screen". Then add you sprites inside this layer.
on this layer override the visit method
- (void) visit
{
glPushMatrix();
glEnable(GL_SCISSOR_TEST);
glScissor(x,y, width, height); //here put the position and the size of the paper/screen
[super visit];
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
}
a sprite reaching the border of the paper/screen will be scissored off.
REMEMBER: glScissor will use PIXEL values not points, so it`s your job to use double values for retina display ( CC_CONTENT_SCALE_FACTOR() can come in handy )

Bspline should be redrawn, without deleting all of the rest of the window

In my mousefunc i call a function bspline. It works like this:
With your mouse you can put controllpoints and according to these points the bspline is drawn.So if you have drawn three points a curve between those points is displayed. By adding another point the old curve disappears and a new one appears. This new one lies now between the four points.This works just fine. BUT: This bspline curve is only displayed in one viewport.This viewport has a black border. This border disappears when my bspline is redrawn. This happens because of calling glutPostredisplay. Because in my glutDisplayFunc i call glClear(GL_COLOR_BUFFER_BIT). So it is the natural thing to happen. If i delete the glClear(GL_COLOR_BUFFER_BIT) in my displayfunc the border stays but the old curves stay too. Even if i say that the border should be redrawn nothing happens. I cant think of an alternative. Would appreciate it if you could help me...
In OpenGL the usual approach is to rerender the whole scene whenever some part of it changes. In your case changing the control points of the B-Spline should trigger a redisplay of the scene instead of perform drawing operations in the mouseclick handler function.
OpenGL has no geometry persistency, it just draws primitves to a pixelbased framebuffer. And as such you must use it.
To clarify, some pseudocode:
BSpline *b_spline;
void on_mouseclick(int x, int y)
{
float x_, y_;
transform_screen_to_scene(x,y, &x_, &y_);
bspline_add_control_point(b_spline, x_, y_);
trigger_redisplay();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
setup_viewport_and_projection();
bspline_draw(b_spline);
swap_buffers();
}