Mouse event wont update camera rotation in winforms - c#

My app has a custom panel used to display the XNA screen within a WinForm. I've currently displayed a test model with no problem and now working on camera movement. My camera is a Free Camera (not bound to look at any specific target), but I've been having trouble getting the mouse to update the yaw and pitch of the camera on its own axis. I thought maybe something was wrong with my update method, but that wasn't the case, because the camera updates moving forward and backwards using KeyboardState. But I have no idea as to why the MouseState isn't working.
FreeCamera.cs
using XNAButtonState = Microsoft.Xna.Framework.Input.ButtonState;
....
MouseState pastMouseState;
private float rotationSpeed_ = 1f / 60f;
private float yaw_, pitch_;
...
private void updateMatrix()
{
Matrix rotationMatrix = Matrix.CreateRotationX(pitch_) *
Matrix.CreateRotationY(yaw_);
Vector3 forward = new Vector3(0, 0, 1);
forward = Vector3.Transform(forward, rotationMatrix);
viewMatrix_ = Matrix.CreateLookAt(Position, Position + forward, Up);
projectionMatrix_ = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4, 16.0f / 9.0f, 0.1f, 100000.0f);
}
private void cameraInput()
{
KeyboardState keyboardState = Keyboard.GetState(); <-- updates
currentMouseState = Mouse.GetState(); <-- not updating
if (currentMouse.LeftButton == XNAButtonState.Pressed)
pitch_ -= rotationSpeed_;
if (keyboardState.IsKeyDown(Keys.W))
move(1);
if (keyboardState.IsKeyDown(Keys.S))
move(-1);
pastMouseState = currentMouseState;
}
public void update()
{
cameraInput();
updateMatrix();

In order to use XNA's mouse API (rather than WinForm events) you must set Mouse.WindowHandle appropriately (MSDN).
If you are using the official samples, then putting this in your MainForm's constructor will do the trick:
Mouse.WindowHandle = this.Handle;
(Of course using Microsoft.Xna.Framework.Input;)

Related

Keeping camera & mouse movement speeds the same on Drag in Unity3D

this question is a bit long but please bare with me.
I am working with Unity3D's newer input system, and I've been able to set up camera movement so when the user clicks the middle mouse button, they can drag the camera around along the X- & Y-axis. However, has a very parallax-y feel to it, which I am trying to avoid. From my understanding this is happening because the speed of the mouse movement & the camera movements are not the same (See, visual).
My InputController class:
public class InputController : MonoBehaviour
{
private InputControls inputControls;
[Header("Camera Movement")]
[SerializeField] CameraController cameraController;
private InputAction.CallbackContext context;
void Awake()
{
inputControls = new InputControls();
}
private void Start()
{
inputControls.Mouse.MiddleButton.started += MiddleButton_started;
}
public void MiddleButton_started(InputAction.CallbackContext ctx)
{
context = ctx;
}
private void Update()
{
bool mouseIsDown = context.performed;
if (mouseIsDown)
{
Vector2 delta = inputControls.Mouse.DeltaPosition.ReadValue<Vector2>();
cameraController.Move(delta, false);
}
}
}
My CameraController class:
public class CameraController : MonoBehaviour
{
[SerializeField] Vector2 minPosition;
[SerializeField] Vector2 maxPosition;
[SerializeField] float speed = 3;
[SerializeField] float zPosition = -10f;
private Camera mainCam;
private void Start()
{
mainCam = GetComponent<Camera>();
}
public void Move(Vector2 deltaPosition, bool convertToViewportPoint = true)
{
if (convertToViewportPoint) deltaPosition = mainCam.ScreenToViewportPoint(deltaPosition);
Vector3 moveTo = new Vector3(deltaPosition.x, deltaPosition.y, 0);
Vector3 target = transform.position - moveTo;
float clampedX = Mathf.Clamp(target.x, minPosition.x, maxPosition.x);
float clampedY = Mathf.Clamp(target.y, minPosition.y, maxPosition.y);
transform.position = Vector3.Lerp(transform.position, new Vector3(clampedX, clampedY, -10), speed * Time.deltaTime);
}
}
Now, in this version of the CameraController, the parallax-y feel might be coming from the fact that the Lerp speed is constant, whereas the speed of the mouse movement is not.
However, I've tested it with various magnitudes (ie., the magnitudes of the delta mouse position, the current mouse position, the current mouse position subtracted from the delta mouse position, etc...) with generally the same results -- either the camera moves too fast for the mouse or vice versa.
From my limited but growing understanding of trig, magnitudes represents the speed of a vector but I CANNOT get the speed of the camera movement and the speed of the mouse movement to match up. I would like for this to happen so that when the user clicks a certain point, that point generally stays under the cursor when in movement (See other visual).
Can someone help me understand how I might achieve this?
Thanks kindly.
What I did to solve this, in case anyone comes across this, is get the position of the cursor when the mouse is first clicked. We can call that dragOrigin. Then we can get the change in position as the mouse moves (polling the input through Unity's input system can be done through an Update function). Then we subtract the new mouse position from dragOrigin. Once we have that difference -- lets call it deltaPosition -- we just add it to the camera's current position.
To ensure that the speed matches that of the mouse, run the deltaPosition and the currentMousePosition through SmoothDamp to get the velocity of the change. Its magnitude will be the speed you want the camera to move.
So for example:
// Call this when the user first clicks the mouse.
// Lets assume this is part of a class that controls the camera.
public void SetDragOrigin(Vector2 mousePosition)
{
// could probably cache Camera.main for efficiency
dragOrigin = Camera.main.ScreenToWorldPoint(mousePosition);
}
// This is called in an update function of another class that deals with input.
// (Or perhaps you can simply pass the input controls to the class controlling the camera, but that's not the case here).
public void Move(Vector2 newMousePosition)
{
mousePosition = mainCam.ScreenToWorldPoint(mousePosition);
Vector2 deltaPosition = dragOrigin - mousePosition;
Vector3.SmoothDamp(deltaPosition, mousePosition, ref dragVelocity, 0.1f, 250f, Time.deltaTime);
cameraPosition += new Vector3(deltaPosition.x, deltaPosition.y, 0) * dragVelocity.magnitude * Time.deltaTime;
// anything else you want to do like clamping camera position
// ....
}
One thing to note here, is that the 5th argument of SmoothDamp is the maxSpeed. I hardcoded it in this example, but you might want to play around with it. When I did not set the maxSpeed, there was some wonky things happening to the dragVelocity over time, so it may be better to set the maxSpeed to what suits you best.

How to continue moving when I decrease speed

I am creating a game that is set in Zero-gravity. I am using a script that can be found here https://github.com/brihernandez/ArcadeSpaceFlightExample
The movement is controlled by a throttle that can be increased/decreased using the mouse wheel or 'w' and 's' keys.
This code controls the mouse wheel and is the same for the 'w' and 's' keys.
private void UpdateMouseWheelThrottle()
{
throttle += Input.GetAxis("Mouse ScrollWheel");
throttle = Mathf.Clamp(throttle, 0.0f, 1.0f);
}
The object that is accelerated by the throttle stops moving when I decrease the throttle and does not drift or continue moving as it should in zero gravity.
This is the code that controls the physics.
public class PlayerPhysics: MonoBehaviour
{
[Tooltip("X: Lateral thrust\nY: Vertical thrust\nZ: Longitudinal Thrust")]
public Vector3 linearForce = new Vector3(100.0f, 100.0f, 100.0f);
[Tooltip("X: Pitch\nY: Yaw\nZ: Roll")]
public Vector3 angularForce = new Vector3(100.0f, 100.0f, 100.0f);
[Range(0.0f, 1.0f)]
[Tooltip("Multiplier for longitudinal thrust when reverse thrust is requested.")]
public float reverseMultiplier = 1.0f;
[Tooltip("Multiplier for all forces. Can be used to keep force numbers smaller and more readable.")]
public float forceMultiplier = 100.0f;
public Rigidbody Rigidbody { get { return rbody; } }
private Vector3 appliedLinearForce = Vector3.zero;
private Vector3 appliedAngularForce = Vector3.zero;
private Rigidbody rbody;
private Player player;
void Awake()
{
rbody = GetComponent<Rigidbody>();
if (rbody == null)
{
Debug.LogWarning(name + ": ShipPhysics has no rigidbody.");
}
player = GetComponent<Player>();
}
void FixedUpdate()
{
if (rbody != null)
{
rbody.AddRelativeForce(appliedLinearForce * forceMultiplier, ForceMode.Force);
rbody.AddRelativeTorque(appliedAngularForce * forceMultiplier, ForceMode.Force);
}
}
public void SetPhysicsInput(Vector3 linearInput, Vector3 angularInput)
{
appliedLinearForce = MultiplyByComponent(linearInput, linearForce);
appliedAngularForce = MultiplyByComponent(angularInput, angularForce);
}
private Vector3 MultiplyByComponent(Vector3 a, Vector3 b)
{
Vector3 ret;
ret.x = a.x * b.x;
ret.y = a.y * b.y;
ret.z = a.z * b.z;
return ret;
}
}
And here is the code that instantiates both.
public class Player: MonoBehaviour
{
public static Player Playerdrone { get { return player; } }
private static Player player;
private PlayerInput input;
private PlayerPhysics physics;
public Vector3 Velocity { get { return physics.Rigidbody.velocity; } }
public float Throttle { get { return input.throttle; } }
// Start is called before the first frame update
void Awake()
{
input = GetComponent<PlayerInput>();
physics = GetComponent<PlayerPhysics>();
}
// Update is called once per frame
void Update()
{
physics.SetPhysicsInput(new Vector3(input.strafe, 0.0f, input.throttle), new Vector3(input.pitch, input.yaw, input.roll));
}
}
I cannot get the Rigidboy to keep drifting when I decrease the throttle, my option is to either abandon the throttle concept and use a simple "Addforce" but I would prefer to keep it.
I have edited the script to have a similar function to the 'Roll' that used 'w' and 's' but moves me forwards and backward. I still find myself decelerating when I let go of the input.
Lowering drag and angular causes the object to spin out of control due to the mouse following function. The mouse does not center the camera and instead causes the camera to keep rotating and tracking the mouse. Changing the mouse script will cause it's own issues since it controls the pitch and yaw of the object.
private void SetStickCommandsUsingMouse()
{
Vector3 mousePos = Input.mousePosition;
// Figure out most position relative to center of screen.
// (0, 0) is center, (-1, -1) is bottom left, (1, 1) is top right.
pitch = (mousePos.y - (Screen.height * 0.5f)) / (Screen.height * 0.5f);
yaw = (mousePos.x - (Screen.width * 0.5f)) / (Screen.width * 0.5f);
// Make sure the values don't exceed limits.
pitch = -Mathf.Clamp(pitch, -1.0f, 1.0f);
yaw = Mathf.Clamp(yaw, -1.0f, 1.0f);
}
The object that is accelerated by the throttle stops moving when I decrease the throttle and does not drift or continue moving as it should in zero gravity.
The Rigidbody component has properties that can be changed in the Inspector (documented here) (and also via script (documented here)):
Mass: The mass of the object (in kilograms by default).
Drag: How much air resistance affects the object when moving from forces. 0 means no air resistance, and infinity makes the object
stop moving immediately.
It sounds like your Drag value might be too large (?).
Although this example project uses "space mechanics" (effectively with no air resistance), the Drag property can still be used to tune the movement and "drift behavior" of the rigidbody, and to control how long it will keep decelerating after the throttle value went down to 0.
Alternatively, you could try to experiment with other ForceMode parameters for the Add*Force() calls: ForceMode.Acceleration or ForceMode.VelocityChange (which is ignoring the mass of the rigidbody).
To make your game more "realistic", you could instead set the Drag property to 0 (no air resistance like in real space -> infinite drift), and try to actively decelerate the ship by experimenting with retrograde rockets/thrusters, by adding a force in backward direction. However, that's probably quite impractical for an arcade game, I guess.
There are also the global Physics settings of the project, which can be found under Edit -> Project settings -> Physics (documented here). There, you'll also find many important physics settings (like Gravity, for example).
(It can also be interesting to have a look at the official docs of the PhysX physics engine, which Unity uses internally for its 3D physics, to understand how things actually work under the hood...)

How to determine an android phones viewing direction when starting an app

I'm working on an Augmented Reality app for Android without tracking images/objects. The user stands at a predefined position and virtual objects are placed into the real world. when the user turns around or moves the phone, the objects are fixed at their respective places. I do this by applying the gyroscope data to the camera.
My problem: I want the objects positions to be always fixed to the same places regardless of the users viewing direction when he starts up the app. Right now, on starting the app, the objects are positioned depending on the camera. After that, they are fixed to their places, when the user changes his viewing direction.
I drew an image of what the exact problem is to better elaborate:
I want to know which sensors are relevant to solve this problem. Since Google Maps accurately determines the viewing direction of a user, I assume there are built in sensors to find out in which direction the user is looking in order to apply this information to the camera's rotation at the start.
This is the code I use to apply the phones rotation to the camera (I'm using Unity and C#):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gyrotransform : MonoBehaviour
{
// STATE
private float _initialYAngle = 0f;
private float _appliedGyroYAngle = 0f;
private float _calibrationYAngle = 0f;
private Transform _rawGyroRotation;
private float _tempSmoothing;
// SETTINGS
[SerializeField] private float _smoothing = 0.1f;
private IEnumerator Start()
{
Input.gyro.enabled = true;
Application.targetFrameRate = 60;
_initialYAngle = transform.eulerAngles.y;
_rawGyroRotation = new GameObject("GyroRaw").transform;
// _rawGyroRotation.parent = Core.Instance.transform;
_rawGyroRotation.position = transform.position;
_rawGyroRotation.rotation = transform.rotation;
// Wait until gyro is active, then calibrate to reset starting rotation.
yield return new WaitForSeconds(1);
StartCoroutine(CalibrateYAngle());
}
private void Update()
{
ApplyGyroRotation();
ApplyCalibration();
transform.rotation = Quaternion.Slerp(transform.rotation, _rawGyroRotation.rotation, _smoothing);
}
private IEnumerator CalibrateYAngle()
{
_tempSmoothing = _smoothing;
_smoothing = 1;
_calibrationYAngle = _appliedGyroYAngle - _initialYAngle; // Offsets the y angle in case it wasn't 0 at edit time.
yield return null;
_smoothing = _tempSmoothing;
}
private void ApplyGyroRotation()
{
_rawGyroRotation.rotation = Input.gyro.attitude;
_rawGyroRotation.Rotate(0f, 0f, 180f, Space.Self); // Swap "handedness" of quaternion from gyro.
_rawGyroRotation.Rotate(90f, 180f, 0f, Space.World); // Rotate to make sense as a camera pointing out the back of your device.
_appliedGyroYAngle = _rawGyroRotation.eulerAngles.y; // Save the angle around y axis for use in calibration.
}
private void ApplyCalibration()
{
_rawGyroRotation.Rotate(0f, -_calibrationYAngle, 0f, Space.World); // Rotates y angle back however much it deviated when calibrationYAngle was saved.
}
public void SetEnabled(bool value)
{
enabled = true;
StartCoroutine(CalibrateYAngle());
}
}
As far as I understand it the Gyroskope returns the rotational difference since it was started.
That's why your objects appear in the direction you are facing during start.
I guess what you rather want might be Compass.magneticHeading at least for setting the correct rotation once at gamestart
// Orient an object to point to magnetic north.
transform.rotation = Quaternion.Euler(0, -Input.compass.magneticHeading, 0);
You could do this once at start on the parent of all the objects you want to show in order to orient them correctly on the GPS north.

Unity C# camera movement

I'm currently trying to create a Camera Control for unity (to follow around a prefab and be able to zoom and such ...
I am very new to C#
an issue I am having with this script is that
camera zooming to 0,0,0. (when I need it to stay at it's current Y-axis, I tried changing the void "Move()" but the vector requires 3 m_....'s
I also need to write a piece of code that will allow the player to zoom the camera in and out using the
scroll wheel... (In "Public Void Update()"...
I've been looking through guides and videos and can't find anything to assist me with this..
this is the section of code I require help with :
private void FixedUpdate()
{
Move();
}
private void Move()
{
m_DesiredPosition = m_target.position;
transform.position = Vector3.SmoothDamp(transform.position,
m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
}
public void Update()
{
// Get the scroll value of the mouse scroll wheel
// float scroll = Input.GetAxis("Mouse ScrollWheel");
// Is the scroll value not 0?
// Modify the orthographic size by the scroll value
Camera.main.orthographicSize = 4.8f;
}
For keeping the camera at Y = 0 simply override Y:
m_DesiredPosition = m_Target.position;
m_DesiredPosition.Y = 0;
transform.position = Vector3.SmoothDamp(transform.position,
m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
For zooming the camera you'll want to add/subtract the value to orthographicsize instead of simply setting it:
// Zoom in
Camera.main.orthographicSize -= 4.8f;
// Zoom out
Camera.main.orthographicSize += 4.8f;

Rotation of a Sprite Around Its Pivot

I have a class below that I attach to a object in order to make it rotate around its pivot. I sent the pivot of the sprite via the inspector.
This works exactly how I want it too, BUT the issue I am having is that whenever I touch and drag it, and then touch and drag it again, it snaps to a new position.
What I would like for it to do is, when it is rotated and then rotated again, the sprite stays in its same rotation and not snap to a new position and I would like the angle of the sprite to be reset to 0. The next then is that I want the angle to continually rotate. So if I rotate it in the positive direction, the angle should keep increasing in the positive direction and not change..Such as 0---> 360 ----> 720 -----> etc, etc. And then when the mouse is released, the sprite stays in the same position but the angle is now set back to 0. And then when clicked again to rotate, it rotates from that exact position.
Here is my code thus far which works well for rotating, but I would like to modify it to achieve the above scenario. Any help with this?
public class Steering : MonoBehaviour {
float prevAngle,wheelAngle,wheelNewAngle = 0;
public SpriteRenderer sprite;
void Start () {
}
void Update () {
}
public float GetAngle(){
return wheelAngle;
}
void OnMouseDrag(){
Vector3 mouse_pos = Input.mousePosition;
Vector3 player_pos = Camera.main.WorldToScreenPoint(this.transform.position);
mouse_pos.x = mouse_pos.x - player_pos.x;
mouse_pos.y = mouse_pos.y - player_pos.y;
wheelNewAngle = Mathf.Atan2 (mouse_pos.y, mouse_pos.x) * Mathf.Rad2Deg;
if (Input.mousePosition.x > sprite.bounds.center.x) {
wheelAngle += wheelNewAngle - prevAngle;
} else {
wheelAngle -= wheelNewAngle - prevAngle;
}
this.transform.rotation = Quaternion.Euler (new Vector3(0, 0, wheelAngle));
Debug.Log (wheelAngle);
prevAngle = wheelNewAngle;
}
void OnMouseUp(){
prevAngle = wheelNewAngle;
wheelAngle = 0;
}
}
By angle of the sprite, do you mean the rotation? I'm not sure how the position is changing if there's nothing in your code doing that. Does it always move to the same position? I'm having a little trouble visualizing how your system is supposed to look but I hope this helps.
It looks like you might want to store the previous mouse position so you can get the relative amount to rotate each frame.
At the top:
Vector3 prevMousePos = Vector3.zero;
This method will help get the position when the player pressed:
void OnMouseDown(){
prevMousePos = Input.mousePosition;
}
Then in OnMouseDrag() get the difference between the two mouse positions to get the relative position (if you moved the mouse left, right, up, or down since pressing):
Vector3 mouseDiff = Input.mousePosition - prevMousePos;
With this it will use the relative mouse position after pressing instead of the current one, which should smooth things out.

Categories

Resources