I am new to SFML and I am just playing around with an example at the moment. I try to draw a sprite where I last clicked my mouse in a window. But some reason the event fires and no sprite is drawn. I can't really figure out why.
I've been looking around in the Documentation but I don't seem to do anything wrong. Help me pretty please? :3
The event is called OnMouseLeftClick and I am trying to draw the mouseLeftClickSprite object. I have removed the bits of code that doesn't matter like the OnMouseMoved event and the OnClose event. They have nothing to do with this.
using System;
using SFML.Audio;
using SFML.Graphics;
using SFML.Window;
namespace SFMLExample
{
class Program
{
static Text xMouseCoord;
static Text yMouseCoord;
static Text statusMsg;
static float mouseX, mouseY;
static Texture mouseLeftClickTexture;
static Sprite mouseLeftClickSprite;
static void OnMouseLeftClick(object sender, EventArgs e)
{
if (Mouse.IsButtonPressed(Mouse.Button.Left))
{
statusMsg.DisplayedString = "Console: OnMouseLeftClick() Fired";
RenderWindow window = (RenderWindow)sender;
mouseLeftClickSprite.Position = new Vector2f(mouseX, mouseY);
window.Draw(mouseLeftClickSprite);
}
}
static void Main(string[] args)
{
mouseX = 0.0f;
mouseY = 0.0f;
// Create the main window
RenderWindow window = new RenderWindow(new VideoMode(800, 600), "SFML Window");
window.Closed += new EventHandler(OnClose);
window.MouseMoved += new EventHandler<MouseMoveEventArgs>(OnMouseMoved);
window.MouseButtonPressed += new EventHandler<MouseButtonEventArgs>(OnMouseLeftClick);
// Load a sprite to display
Texture texture = new Texture("cute_image.jpg");
Sprite sprite = new Sprite(texture);
mouseLeftClickTexture = new Texture("GM106.png");
mouseLeftClickSprite = new Sprite(mouseLeftClickTexture);
// Create a graphical string to display
Font font = new Font("arial.ttf");
Text text = new Text("Hello SFML.Net", font);
xMouseCoord = new Text("Mouse X: ", font);
yMouseCoord = new Text("Mouse Y: ", font);
statusMsg = new Text("Console: ", font);
// Play some Music
//Music music = new Music("nice_music.mp3");
//music.Play();
// Start the game loop
while (window.IsOpen())
{
// Process events
window.DispatchEvents();
// Clear screen
window.Clear();
sprite.Scale = new Vector2f(1.0f, 1.0f);
sprite.Position = new Vector2f(window.Size.X / 2, window.Size.Y / 2);
// Draw the sprite
window.Draw(sprite);
// Draw the strings
xMouseCoord.Position = new Vector2f(text.Position.X, text.Position.Y + 30);
yMouseCoord.Position = new Vector2f(xMouseCoord.Position.X, xMouseCoord.Position.Y + 30);
statusMsg.Position = new Vector2f(yMouseCoord.Position.X, yMouseCoord.Position.Y + 30);
window.Draw(text);
window.Draw(xMouseCoord);
window.Draw(yMouseCoord);
window.Draw(statusMsg);
// Update the window
window.Display();
}
}
}
}
Added this bit below for some clarification
static void OnMouseMoved(object sender, EventArgs e)
{
RenderWindow window = (RenderWindow)sender;
Vector2i vector = window.InternalGetMousePosition();
mouseX = vector.X;
mouseY = vector.Y;
xMouseCoord.DisplayedString = "Mouse X: " + mouseX;
yMouseCoord.DisplayedString = "Mouse Y: " + mouseY;
statusMsg.DisplayedString = "Console: ";
}
You only draw your sprite once. With approximately 60 frames per second, you might see it blink... or not. If you need it to stay permanent, you need to remember the point in your main program and then draw a sprite at that point every time your draw the other things.
You may want to look into game programming patterns. Normally, you have three big blocks that are repeated endlessly: you get user input, you act on user input, you draw the result.
In pseudo code:
while(isRunning)
{
HandleUserInput();
ChangeWorld():
Render();
}
That way, you can check yourself if what you do is correct. For example, in your scenario you would have drawn something while handling user input. Not good.
The events are dispatched and evaluated inside the so-called window.DispatchEvents(). When you click on your window, the event will be fired and your sprite will be drawn.
The thing is that you are calling window.Clear() after it, so you can not see the result. As nvoigt said, you must render everything at each frame, that is to say at each turn of your loop. This is why one uses Clear() method from Window, to clear the result of the previous frame.
What you could do is remember the position of the mouse when the event is fired or directly set the position of the sprite according to it. Then draw your sprite along with your text etc. If you don't want it to be displayed before any click of the mouse, just put its coordinates at a point outside the window so it will not be drawn.
Related
I am working in my own editor for making WPF forms. My issue is that I am having the worst time selecting multiple controls (buttons, labels, etc) and dragging them smoothly across the main window. My application chokes when I try to drag, oh say, 20 selected buttons at the same time.
I found that the culprit is the fact that I am drawing multiple rectangles for each object as they are being dragged and this function is being called in the MouseMove event.
void ControlObjectControl_MouseMove(object sender, MouseEventArgs e)
{
if (newPosition != oldPosition)
{
DragSelection(newPosition);
}
}
private void DragSelection(Point newPosition)
{
foreach (FrameworkElement item in designer.SelectionService.CurrentSelection)
{
if (item is ObjectControl)
(item as ObjectControl).m_ParentControlObject.Position = new System.Drawing.Rectangle() { X = X, Y = Y, Width = (int)item.Width, Height = (int)item.Height };
//..There's code that calculates the item's position and dimensions
}
}
How do I make it to where it only draws the rectangle once and I am still able to see my selected object(s) move smoothly when I drag them?
I did something similar in my application except I used a TranslateTransform to move my elements. Each "frame" (every mouse move) that I was dragging, I got the position of the mouse and compared that to the previous position of the mouse. I would then set a new TranslateTransform X/Y values equal to the X/Y mouse position change and then would give that to the RenderTransform of each object I wanted to move. Something like:
void ControlObjectControl_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
// Get the change in Location
mouseLocation = Mouse.GetPosition();
Point deltaLocation = mouseLocation - previousLocation;
// Make a new transform
TranslateTransform transform = new TranslateTransform();
transform.X = deltaLocation.X;
transform.Y = deltaLocation.Y;
// Apply the transform
// foreach thing
thing.RenderTransform = transform;
// set previous location
previousLocation = mouseLocation;
}
}
Now your objects only get drawn once and only their positions get changed. Hope this helps
If I am making two UserControls move in C# (WPF application in Visual Studio), I want to be able to detect collision between the two of them.
I currently am using the "doesIntersect" method, with an if statement to show a message box that says "Collision!."
It works fine, however, the message box doesn't show until the code is done executing, which makes sense for how I have it coded. Is there any way to get the message box to show as soon as they collide and not after they have already passed each other? Would I have to implement asynchronous programming here?
I currently am using a method for movement of the one object:
public static void MoveTo(UserControl FoodX, double X, double Y)
{
Vector offset = VisualTreeHelper.GetOffset(FoodX);
var top = offset.Y;
TranslateTransform trans = new TranslateTransform();
FoodX.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, 200, TimeSpan.FromSeconds(2));
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
}
Then I have my collision code, where I put an invisible rect behind the image so I can use the Rect method:
public static Rect rectbox;
public bool Collision()
{
rectbox = new Rect(Canvas.GetLeft(rect1), Canvas.GetTop(rect1), rect1.Width, rect1.Height);
bool doesIntersect = rect1.RenderedGeometry.Bounds.IntersectsWith(FoodImage.rectfood);
return doesIntersect;
}
I'm far from being an expert on WPF animations, but since nobody replied so far...
TranslateTransform has a Changed event that fires while the animation is moving the UI element. You can subscribe to this event with an event handler method, check for collision in your event handler, and if you detect collision, you can execute what you want.
The following code worked for me, i.e. the message box was shown instantly when the moving element collided with another element (I tested it with a Canvas containing a moving UserControl and another, static element):
public void MoveTo(UserControl FoodX, double X, double Y)
{
Vector offset = VisualTreeHelper.GetOffset(FoodX);
var top = offset.Y;
TranslateTransform trans = new TranslateTransform();
FoodX.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, 200, TimeSpan.FromSeconds(2));
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
//Added line
trans.Changed += RecalculateCollision;
}
public void RecalculateCollision(object sender, EventArgs e)
{
Rect r1 = BoundsRelativeTo(MovingBox, Canvas);
Rect r2 = BoundsRelativeTo(StaticBox, Canvas);
if (r1.IntersectsWith(r2))
{
MessageBox.Show("Collided");
}
}
public static Rect BoundsRelativeTo(FrameworkElement element, Visual relativeTo)
{
return element.TransformToVisual(relativeTo).TransformBounds(new Rect(element.RenderSize));
}
I am doing a project about a chess-like game, so players own their pawns on the map. Every time player decides to move a pawn, he needs to get a number from a dice, then that pawn would move according to the rolled number. Move function of pawns is finished, but I didn't show the moving process for them.
I have one panel for map and four panels for starting base(holding the pawns at the beginning of the game).
GUI for gameboard
In paint event, I ask the system to draw everything.
private void P_Map_Paint(object sender, PaintEventArgs e)
{
manager.VisualizeCollection(map, gr_map);
manager.VisualizeStartingBase(bases, grs);
manager.VisualizePawns(manager.Players, grs, gr_map);
manager.DisplayAvailablePawn(gr_map, grs);
manager.DisplaySelectedPawn(gr_map, grs);
}
For every move of a pawn, I am trying to use a timer to make the image moving on the screen.
public void DoMovement(Pawn pawn)
{
if (TargetSpot != null)
{
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
EventHandler t_tick = null;
Point targetP = TargetSpot.Location;
Point currentP = pawn.ImageLocation;
int XMove = (targetP.X - currentP.X) / 10;
int YMove = (targetP.Y - currentP.Y) / 10;
Rectangle drawRange = new Rectangle(pawn.ImageLocation.X - (int)(0.5 * pawn.Image.Width),
pawn.ImageLocation.Y - (int)(0.5 * pawn.Image.Height), pawn.Image.Width, pawn.Image.Height);
t_tick = (senders, args) =>
{
if (currentP.X > targetP.X - XMove || currentP.X < targetP.X + XMove)
{// if the image of the pawn doesn't reach the destination
//we keep moving it and ask the pawn to redraw the old place
pawn.ImageLocation = new Point(currentP.X + XMove, currentP.Y + YMove);
pawn.CurrentPanel.Invalidate(drawRange);
}
else
{
pawn.CurrentLocation.LeaveAPawn(pawn);
pawn.CurrentLocation = TargetSpot;
TargetSpot.AddAPawn(pawn);
pawn.CurrentPanel.Invalidate(drawRange);
}
};
t.Tick += t_tick;
t.Interval = 300;
t.Start();
}
}
This is not working fine, everything on the panel is still redrawing. Why?
Excepting the project, I do have a question about the invalidating a region. Like I told the rules of drawing to paint event, then the panel invalidates itself. When we are going to invalidate a region, how the system knows the rule about drawing?
I have three question regarding ILPanel in ILNumerics:
How can I disable the double click interaction of ILPanel to reset the view.
How can I increase the speed of panning using right click and drag. The panning is very slow especially when the shape is vary large.
How can access the elements that I add to a camera and modify them.
Here is a simple example. Two spheres are added to a camera and a ILLable, "Hello", is on top of one of them (the blue one). I want the "Hello" label to jump on top of the green sphere if I rotate the scene with mouse and the green sphere become closer to the camera and visa verse. And, how can I change the color of the spheres say by clicking a button?
private void ilPanel1_Load(object sender, EventArgs e)
{
var scene = new ILScene();
var cam = scene.Camera;
var Sphere1 = new ILSphere();
Sphere1.Wireframe.Visible = true;
Sphere1.Fill.Color = Color.Blue;
Sphere1.Wireframe.Color = Color.Blue;
Sphere1.Transform = Matrix4.Translation(0, 0, 0);
var Sphere2 = new ILSphere();
Sphere2.Wireframe.Visible = true;
Sphere2.Transform = Matrix4.Translation(2, 0, 0);
cam.Add(Sphere1);
cam.Add(Sphere2);
cam.Add(new ILLabel("Hello")
{
Font = new System.Drawing.Font(FontFamily.GenericSansSerif, 8),
Position = new Vector3(0, 0, 1.1),
Anchor = new PointF(0, 1),
});
ilPanel1.Scene = scene;
}
Create a label, which is always on top of other 3D objects
cam.Add(
new ILScreenObject() {
Location = new PointF(0.5f, 0.5f),
Children = {
new ILLabel("Hello")
}
});
See also: http://ilnumerics.net/world3d-and-screen2d-nodes.html
Regarding your "jumping sphere" example: if your want your label to jump to another group (spheres are groups actually) at runtime, you will have to implement that logic into a function which is called at runtime. ilPanel1.BeginRenderFrame might be a good place to check for such updates.
Disable default behavior for the first camera:
scene.Camera.MouseDoubleClick += (_, arg) => {
arg.Cancel = true;
};
See: http://ilnumerics.net/mouse-events.html
React on a button press:
button1.Click += (_,args) => {
ilPanel1.Scene.First<ILSphere>().Fill.Color = Color.Red;
ilPanel1.Refresh();
};
Finding objects in the scene:
see: http://ilnumerics.net/nodes-accessing-managing.html
Controlling the panning speed:
There is no easy way to configure the speed currently. The best way would be to implement the corresponding mouse handler on your own:
Override MouseMove for the camera node.
Implement the panning similar to the way it was done in ILNumerics (see: ILCamera.OnMouseMove in the sources).
Increase the scale factor.
Dont forget to cancel the event processing at the end of your handler.
I'm busy with a small application in which I want to display information at the location of the cursor when it hoovers over a Canvas. The Canvas in question is a custom one (inherited from Canvas) which provides functionality to add DrawingVisuals (as shown in basically every tutorial on displaying large amounts of geometric shapes on a canvas).
I would like to display a vertical line and horizontal line as well as the local coordinates (p in the code below) which are directly derived from the canvas coordinates (v). At the moment I'm rendering these objects at position (0,0) and use offset during the OnMouseMove event to update their location.
The horizontal and vertical lines are rendered in the DrawingVisual _cursor and the location in local y,z-coordinates in _info.
private void oCanvas_MouseMove(object sender, MouseEventArgs e)
{
#region 1. Get location data
System.Windows.Vector v = (System.Windows.Vector)e.GetPosition(oCanvas);
// point in YZ coordinates
BSMath.DoubleXY p = new BSMath.DoubleXY();
p.X = (oCanvas.OriginY - v.Y) / oCanvas.ZoomFactor;
p.Y = (oCanvas.OriginX - v.X) / oCanvas.ZoomFactor;
#endregion
#region 2. Update cursor and info
if (oSettings.ShowInformation)
{
_info.Info = p.X.ToString("0.0") + " | " + p.Y.ToString("0.0");
_info.Render(0, 0);
_info.Visual.Offset = v;
}
// move cursor
_cursor.Visual.Offset = v;
}
Using the mousemove event seems to be creating a lot of overhead and I can see that there are issues tracking the mouse movements when I move the mouse quickly.
Can anyone recommend a better way of creating the same effect?
example http://www.iccg.be/test/images/canvas.jpg
Edit:
I investigated it a bit further and the problem seems to occur when the resolution of the canvas is bigger. If it is a 600x400 canvas then there is no delay, but when it is around 1000x800 I get the problem with delays when hoovering. The performance also improves if I use user drawn crosshairs instead of the lines that have the full width/ height of the canvas.
I recently have built something similar and haven't had any performance issues.
Did it the very simple way by adding the stuff directly on the canvas.
The drawn items are in a second canvas behind the mouse position canvas. Both reside in a Grid.
This is for sure not the most sophisticated way to solve this, but it works quite well for me.
Here's the code:
private Point _previous;
private Point _current;
private Line _xLine;
private Line _yLine;
private TextBlock _displayTextBlock;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_current = e.GetPosition(myCanvas);
if (_previous != _current)
{
if (_xLine == null)
{
_xLine = new Line() {X1 = 0, X2 = myCanvas.ActualWidth, Stroke = new SolidColorBrush(Colors.Black)};
_yLine = new Line() {Y1 = 0, Y2 = myCanvas.ActualHeight, Stroke = new SolidColorBrush(Colors.Black)};
_displayTextBlock = new TextBlock();
myCanvas.Children.Add(_xLine);
myCanvas.Children.Add(_yLine);
myCanvas.Children.Add(_displayTextBlock);
}
_displayTextBlock.SetValue(Canvas.TopProperty, _current.Y);
_displayTextBlock.SetValue(Canvas.LeftProperty, _current.X);
_displayTextBlock.Text = _current.X.ToString() + " | " + _current.Y.ToString();
_xLine.Y1 = _current.Y;
_xLine.Y2 = _current.Y;
_yLine.X1 = _current.X;
_yLine.X2 = _current.X;
_previous = _current;
}
}