I made a simple test in xna 2D sprite and tried to move it with mouse, it works but if I move it a bit fast the sprite is lost on the way, I keep the left button pressed and when I'm back to the sprite the drag continues...
I wonder why if I moved a bit fast I lose my sprite???
Here is my move logic :
MouseState ms = Mouse.GetState();
if ((ButtonState.Pressed == Mouse.GetState().LeftButton) && myBall.RectObject.Intersects(new Rectangle(ms.X, ms.Y, 0, 0)))
{
myBall.RectObject = new Rectangle(ms.X - myBall.RectObject.Width / 2, ms.Y - myBall.RectObject.Height / 2, myBall.RectObject.Width, myBall.RectObject.Height);
}
I suggest handling it like this:
if the mouse is left-clicked on the ball (Using the if statement in the OP), then mark the ball as being dragged (A simple 'bool dragged;' on the object)
If the mouse is not left clicked at any time regardless of location, mark the ball as not being dragged.
If the ball is being dragged, jump to the mouse position (Using the code within the if block in the OP)
(All in the same function you are already using)
Edit: here's some sample code in case I didn't explain clearly
MouseState ms = Mouse.GetState();
if ((ButtonState.Pressed == Mouse.GetState().LeftButton))
{
if (myBall.RectObject.Intersects(new Rectangle(ms.X, ms.Y, 0, 0)))
{
myball.dragged = true;
}
}
else
{
myball.dragged = false;
}
if (myball.dragged)
{
myBall.RectObject = new Rectangle(ms.X - myBall.RectObject.Width / 2, ms.Y - myBall.RectObject.Height / 2, myBall.RectObject.Width, myBall.RectObject.Height);
}
Related
I'm trying to create a OpenGL C# project to zoom in and out for a scene. I want to enter and exit fom zoom mode when I press the Z key. And then, when I'm in zoom mode ( Z key pressed first time ) to use the mouse wheel to zoom in / out the scene. When I finished the zoom action, I want to be able to exit the zoom mode ( press Z again ) and then the mouse wheel will stop zooming my scene. Thanks for help!
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
Matrix4 lookat = Matrix4.LookAt(eyeVector, targetVector, upVector);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref lookat);
KeyboardState keyboard = Keyboard.GetState();
if (keyboard[Key.Z] && !keyboard.Equals(lastKeyPress))
{
}
if (keyboard[Key.Z] && !keyboard.Equals(lastKeyPress))
{
}
lastKeyPress = keyboard;
}
This is the function for zoomOut:
public Vector3 zoomOut(Vector3 actual)
{
if(zoomLimits[1]<=actual.X || zoomLimits[1] <= actual.Y || zoomLimits[1] <= actual.Z) {
Console.WriteLine("Limita de zoomOut a fost atinsa!");
return actual;
}
Vector3 nou = new Vector3(actual.X + 5, actual.Y + 5, actual.Z + 5);
return nou;
}
Here I just change the eyeVector from the lookat matrix. But I have some limits. If I exceed those limits the scene will dissapear. Do you have any ideas to solve this? I dont want limits for zoom.
There's only so much help I can give without seeing your full code, but have you checked the clipping planes? That sounds like a textbook case of having a clipping plane too close; check your perspective matrix.
When you call Matrix4.CreatePerspectiveFieldOfView, increase depthFar, that should fix your issue.
Here is a class file I have for button creation:
class Button
{
Texture2D buttonTexture;
Rectangle buttonRectangle;
Color buttonColour = new Color(255, 255, 255, 255);
public Vector2 size, buttonPosition;
public Button(Texture2D newButtonTexture, Vector2 newSize)
{
buttonTexture = newButtonTexture;
size = newSize;
}
public void setPosition(Vector2 newButtonPosition)
{
buttonPosition = newButtonPosition;
}
bool down;
public bool isClicked;
public void Update(MouseState mouse)
{
buttonRectangle = new Rectangle((int)buttonPosition.X, (int)buttonPosition.Y, (int)size.X, (int)size.Y);
Rectangle mouseRectangle = new Rectangle(mouse.X, mouse.Y, 1, 1);
if (mouseRectangle.Intersects(buttonRectangle))
{
if (buttonColour.A == 255)
down = false;
if (buttonColour.A == 0)
down = true;
if (down)
buttonColour.A += 3;
else
buttonColour.A -= 3;
if (mouse.LeftButton == ButtonState.Pressed)
isClicked = true;
}
else if (buttonColour.A < 255)
{
buttonColour.A += 3;
isClicked = false;
}
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(buttonTexture, buttonRectangle, buttonColour);
}
}
Below is how I create the buttons in LoadContent (Ignore the shabby parameters it's just the way I'm currently doing it):
btnResume = new Button(Content.Load<Texture2D>("Buttons/button_Resume"), new Vector2(GraphicsDevice.Viewport.Width / 1.875f, GraphicsDevice.Viewport.Height / 4));
btnResume.setPosition(new Vector2(GraphicsDevice.Viewport.Width / 2 - (0.5f*btnResume.size.X), GraphicsDevice.Viewport.Height / 3));
btnQuit = new Button(Content.Load<Texture2D>("Buttons/button_Exit"), new Vector2(GraphicsDevice.Viewport.Width / 1.875f, GraphicsDevice.Viewport.Height / 4));
btnQuit.setPosition(new Vector2(GraphicsDevice.Viewport.Width / 2 - (0.5f*btnQuit.size.X), GraphicsDevice.Viewport.Height - (GraphicsDevice.Viewport.Height / 3)));
And this is how I draw the two buttons in the draw function of Game1.cs:
case GameState.Menu:
btnResume.Draw(spriteBatch);
btnQuit.Draw(spriteBatch);
break;
This all works fine. For some reason however, when doing the same thing for a "Play" button to put on a new game state called "MainMenu", the button doesn't show up (I have tested putting the button code in the game state "menu" and the button does appear, it just won't show when in any other game state other than "menu").
Does anybody know why it won't work? I've remembered to create the button, set the position, and then draw it within the "case GameState.MainMenu" part of the draw function in Game1.cs, so I honestly have no idea why it isn't working. As a side note, I've tried drawing buttons for another game state called "CharacterSelection" and that doesn't work either, HOWEVER it does work if I use spriteBatch.Draw (so I'm pretty sure it's a problem with the button class' draw function.
I can provide screenshots of code or in-game elements, and I'm sorry if the code is messy, I tend to do that a lot. This is my first XNA game and I've not coded in c# much in the past so a lot of this is new to me.
EDIT:
Here are all my gamestates:
enum GameState
{
Opening,
MainMenu,
CharacterSelection,
Countdown,
Playing,
Menu,
Options,
}
GameState CurrentGameState = GameState.Opening;
Opening - Opening cinematic Main Menu - Title Screen basically Character Selection - select what character to play as. Countdown - 3,2,1 GO before the game starts. Playing - Game is active, players are playing e.t.c Menu - Pause menu (opens when esc is pressed) Options - When "Options" is pressed in the pause or main menu (Hasn't been set up yet).
Thanks for the help, but I've solved it. Silly mistake by me! Forgot "btnPlay.Update(mouse);" in my update function! -__-
I'm working on a 2D game where the player can drag tiles around. It works in a way that the player clicks and hold a tile and depending in which direction the player moves the mouse from then on, the drag direction is decided.
The problem however is that this is overly sensitive. It might often be the case that the player starts dragging and wanted to drag vertically but due to the mouse sensitivity it turns out to drag horizontally (or vice versa).
Does anyone have an idea how to add a tolerance threshold to this dragging behavior? The relevant part in my code looks basically like this:
private void Update()
{
if (_isMouseDown && sourceTile != null)
{
_isDraggingTile = true;
/* Determine drag direction and drag target cell. */
Vector3 dragDistance = Input.mousePosition - _originMousePosition;
dragDistance.Normalize();
if (_dragDirection == DragDirection.None)
{
float f = Vector3.Dot(dragDistance, Vector3.up);
/* Mouse up drag. */
if (f >= 0.5f)
{
_dragDirection = DragDirection.Up;
_dragTargetCell = sourceTile.gridCell.upNeighbor;
}
/* Mouse down drag. */
else if (f <= -0.5f)
{
_dragDirection = DragDirection.Down;
_dragTargetCell = sourceTile.gridCell.downNeighbor;
}
else
{
/* Mouse right drag. */
f = Vector3.Dot(dragDistance, Vector3.right);
if (f >= 0.5f)
{
_dragDirection = DragDirection.Right;
_dragTargetCell = sourceTile.gridCell.rightNeighbor;
}
/* Mouse left drag. */
else if (f < -0.5f)
{
_dragDirection = DragDirection.Left;
_dragTargetCell = sourceTile.gridCell.leftNeighbor;
}
}
}
if (_dragTargetCell != null)
{
// Take care of moving the dragged tile!
}
}
}
Simply delaying the calculation of dragDistance by some frames doesn't turn out to work very well. I think what is needed is a solution to figure out the mouse movement and decide on which axes it moves farthest. Determining the drag direction as above will probably never work out well.
The problem with any collection of information is noise. In your case, the noise is defined by the wrong movement of the user. Nonetheless, it should be possible to minimize the effect of noise by averaging the values.
There are advanced algorithms used in DSP but I guess a basic averaging of the info should do in your case.
What you could try is that instead of moving in Update at once like you do, collect movement over several frames, then average all those frames and see if it goes better:
IEnumerator GetAverageMovement(Action<Vector3> result)
{
int frames = 0;
List<Vector3>list = new List<Vector3>();
while(frames < 30f) // half a second appr
{
list.Add(GetDirection());
frames++;
yield return null;
}
result(AverageAllValues());
}
GetDirection is just returning the delta between current and previous position, AverageAllValues simply adds all values in list and divides by list.Count (aka 30).
This should fix cases when the user move all the way right but a bit up at the end. The last bit should be canceled by the large right movement.
If that is still not enough, then you could add some logic within the method that if a value is too far gone from the average, discard it. I don't think you need this in there.
I think you should create a Queue of positions with limited size right after dragging .
by comparing final value of Queue and first value you can find the direction.
If you want to get better results you can get Variance of positions and get better results.
for my programm im programming a "clock like" behaviour. Meaning i
order some pictures in a circle (works)
if i click and drag on any item all items should rotate with the mouse (works)
But i get a weird bug. The first time i click and hold the mouse my images "jump" to different positions. if i hold the mouse down i can rotate my clock ust fine.
When i MouseUp and start dragin from the same image it works well. if i go to another image i get this "Jump" again.
When i only have a few images on my clock i see that it doesnt jump. but the start position seems to bee off.
When i only have one item, i can rotate it in a circle, but the moment i start to rotate it jumps away from my mouse and than i can rotate it as desired.
For me it seems to be a wrong "starting point" when first dragging an item. SInce it works fine when i than drag the same item again and again.
Unfortunately i cant find the damn bug, and im searching the whole day already.
#
public void SetLayoutHorizontal ()
{
Debug.Log ("LAYOUT");
for (var i =0; i < Rect.childCount; i++)
{
var PanelPrefab = Rect.GetChild (i) as RectTransform;
Transform ImageObject = PanelPrefab.GetComponentInChildren<Transform>().Find("Image");
if (PanelPrefab == null)
continue;
PanelPrefab.sizeDelta = CellSize;
PanelPrefab.anchoredPosition = new Vector2(radius * Mathf.Sin( CalculateCircleAngle(i) - deltaRadian),radius * Mathf.Cos(CalculateCircleAngle(i) - deltaRadian));
}
}
private float CalculateCircleAngle(int parts)
{
//parts == Number of parts the whole circle is to be cut into
return parts * (360/Rect.childCount) * (Mathf.PI/180);
}
public void OnDrag (PointerEventData eventData)
{
var diffX = eventData.position.x - _rect.rect.width/2; // MouseX - CenterScreenX
var diffY = eventData.position.y - _rect.rect.height/2; // MouseY - CenterScreenY
deltaRadian = Mathf.Atan2(diffY,diffX);
SetDirty();
}
Edit:
Ok i Edited the code but it still is not working.
I added the following method:
public void OnBeginDrag(PointerEventData eventData)
{
originalX = eventData.position.x;
originalY = eventData.position.y;
}
and i changed the drag method acordingly
public void OnDrag (PointerEventData eventData)
{
var diffX = eventData.position.x - originalX;
var diffY = eventData.position.y - originalY;
deltaRadian = Mathf.Atan2(diffY,diffX);
SetDirty();
}
The "Jumping" at the beginning of my drag event is gone, but the speed of the draggin is not on par with my mouse.
The closer i am to my starting point of the drag, the faster it moves, the further away i am the slower it gets.
I dont know if this brought me closer to a solution or further away :(
Edit2:
Ok i think the problem might be that my calculations were all done from the center point of view as 0,0 point.
Unity has the bottom left point as 0,0. So i somehow have to translate all those coordiantes first...
All that was needed was a transformation to kartesian coordinates
//Convert to kartesian coordinates with 0,0 in center of screen
var diffX = (eventData.position.x - _rect.rect.width / 2) % (_rect.rect.width / 2);
var diffY = (eventData.position.y - _rect.rect.height / 2) % (_rect.rect.height / 2);
and an addition of the delta instead of the subtraction
PanelPrefab.anchoredPosition = new Vector2(radius * Mathf.Sin(CalculateCircleAngle(i) + deltaRadian),radius * Mathf.Cos(CalculateCircleAngle(i) + deltaRadian));
Your mouse coordinates are relative to the control you click in. You could re-calculate them using the control's position within its container or you use absolute coordinates.
I don't think you are displaying all the relevant code, but your OnDrag function, specifically diffX, and diffY probably provide the answer for you.
I believe you're taking the absolute coordinates of the location you pick, and using them to generate your angle - which means that almost anywhere you click is a big jump from however the current angle is set. Your initial click handler should save off a starting coordinate, and your OnDrag should compare itself against that initial coordinate, based on how far you've dragged from that saved location.
I'm developing a 2D game with XNA game studio 4.0 and I need to make my "Hero" sprite shoot a shot sprite, which is a rectangle.
When I press left control to shoot, the shot is starting from my player. So far, it's ok, but the problem is that it never stops - its position never goes to theVoid.
Here is my code for shooting:
protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.LeftControl) && isShotted == false)
{
isShotted = true;
shotPosition = playerPosition;
}
if (isShotted == true && (shotPosition.X <= shotPosition.X+150) )
{
shotPosition.X += shotSpeed.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
else
{
isShotted = false;
shotPosition = theVoid;
}
}
Some clarification:
playerPosition is my "Hero" sprite position.
theVoid is Vector2 (700,700), when I set shotPosition = theVoid the shot dissapears.
The shot never disapears because you are updating shotPosition.x every update. You are checking:
if (isShotted == true && (shotPosition.X <= shotPosition.X+150) )
And then inside that if you increment shotPosition.X:
shotPosition.X += shotSpeed.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
One option to fix this would be to check shotPosition.X against the player position - per #jonhopkins comment. If the player can move near the same speed as the shot though, they could just follow it and then the shot would never disappear, this may or may not be what you want.
Your other option is to store the position of where the shot was fired, and compare something like:
if (isShotted == true && (shotPosition.initialX+150 >= shotPosition.currentX) )
Make sure you think about this in terms of how your players and objects move around the coordinate system though. If your player is always stationary in regards to the x-axis that could simplify things compared to if they can run around the screen..