I'm creating a 2D game based on asteroids. And In that game I need to thrust the ship in a direction.
I'm able draw the ship, so that it turns around. But when it comes to moving it forward my problem occours.
I don't seem to be able to get my head around this. (This whole making games thing is new to me ^^)
The player.cs
protected Vector2 sVelocity;
protected Vector2 sPosition = Vector2.Zero;
protected float sRotation;
private int speed;
public Player(Vector2 sPosition)
: base(sPosition)
{
speed = 100;
}
public override void Update(GameTime gameTime)
{
attackCooldown += (float)gameTime.ElapsedGameTime.TotalSeconds;
// Reset the velocity to zero after each update to prevent unwanted behavior
sVelocity = Vector2.Zero;
// Handle user input
HandleInput(Keyboard.GetState(), gameTime);
if (sPosition.X <= 0)
{
sPosition.X = 10;
}
if (sPosition.X >= Screen.Instance.Width)
{
sPosition.X = 10;
}
if(sPosition.Y <= 0)
{
sPosition.Y = 10;
}
if (sPosition.Y >= Screen.Instance.Height)
{
sPosition.Y = 10;
}
// Applies our speed to velocity
sVelocity *= speed;
// Seconds passed since iteration of update
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
// Multiplies our movement framerate independent by multiplying with deltaTime
sPosition += (sVelocity * deltaTime);
base.Update(gameTime);
}
private void HandleInput(KeyboardState KeyState, GameTime gameTime)
{
if (KeyState.IsKeyDown(Keys.W))
{
//Speed up
speed += 10;
sVelocity.X = sRotation; // I know this is wrong
sVelocity.Y = sRotation; // I know this is wrong
}
else
{
//Speed down
speed += speed / 2;
}
if (KeyState.IsKeyDown(Keys.A))
{
//Turn left
sRotation -= 0.2F;
if (sRotation < 0)
{
sRotation = sRotation + 360;
}
}
if (KeyState.IsKeyDown(Keys.D))
{
//Turn right
sRotation += 0.2F;
if (sRotation > 360)
{
sRotation = sRotation - 360;
}
}
}
Am I close, or seriously far from right?
sRotation is an angle, sVelocity is a velocity. You need trigonometry.
for instance, you could use something like that (I didn't test the signs for correctness):
if (KeyState.IsKeyDown(Keys.W))
{
//Speed up
speed += 10;
sVelocity.X = Math.cos(sRotation * 2 * Math.PI / 360);
sVelocity.Y = -Math.sin(sRotation * 2 * Math.PI / 360);
}
Would that solve your problem?
EDIT: your "speed down" formula is wrong. You are currently adding speed/2 with speed, you should have something along:
speed = speed / 2; // note the "=", not "+="
Also, it would probably be preferable to use something like:
if (speed > 0) {
speed -= 5;
} else {
speed = 0;
}
Related
I have a fish which is swimming across the screen. When it gets within 10% of the edge of the screen, I want it to turn begin turning around until it has completely reversed and is now swimming in the opposite direction. This should be more gradual like a fish would swim. I don't want it to rotate on its own axis.
I'm not having any luck getting it to turn around. It only turns partially.
Update
Here's the fish
public class FishSwim : MonoBehaviour
{
public enum Direction { LeftToRight, RightToLeft };
public Direction moveDirection;
[SerializeField]
private float speedMin = 0.5f;
[SerializeField]
private float speedMax = 1f;
[SerializeField]
private bool useOnlySpeedMax = false;
private float speed;
[HideInInspector]
public float removeBeyond;
private void Start()
{
var dist = (transform.position - Camera.main.transform.position).z;
if (moveDirection == Direction.RightToLeft)
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x;
else
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x + FindObjectOfType<SkinnedMeshRenderer>().bounds.size.x;
}
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
if (moveDirection == Direction.RightToLeft)
{
speed = -speed;
}
}
// Update is called once per frame
void Update()
{
float realSpeed = speed * Time.deltaTime;
transform.position += Vector3.right * realSpeed;
if (moveDirection == Direction.RightToLeft && transform.position.x < -Mathf.Abs(removeBeyond))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), Time.deltaTime * 40f);
moveDirection = Direction.LeftToRight;
}
else if (moveDirection == Direction.LeftToRight && transform.position.x > Mathf.Abs(removeBeyond))
{
}
}
}
I would use Coroutines to do this. Explanation in comments:
bool isTurning = false;
[SerializeField] float turnPeriod = 0.5f; // how long it takes to turn, smaller=faster
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
// Get rid of negative speed
}
void Update()
{
float realDistance = speed * Time.deltaTime;
transform.position += transform.right * realDistance;
// Surely there is a better way than calculating
// Mathf.Abs(removeBeyond) every frame... but that is off-topic
if (!isTurning && (
(moveDirection == Direction.RightToLeft
&& transform.position.x < -Mathf.Abs(removeBeyond)
) || (moveDirection == Direction.LeftToRight
&& transform.position.x > Mathf.Abs(removeBeyond)
) ) )
{
// If we aren't already turning and we should be, start turning:
StartCoroutine(Turn());
}
}
IEnumerator Turn()
{
isTurning = true;
Vector3 startForward = transform.forward;
// find end speed and direction
Direction endDirection = moveDirection == Direction.RightToLeft ?
Direction.LeftToRight:Direction.RightToLeft;
// keep track of how much of our turning time has elapsed
float elapsedTimePortion = Time.deltaTime/turnPeriod;
// turn until you've spent enough time turning
while (elapsedTimePortion < 1f)
{
// by whatever portion we've spent time turning, turn starting forward
// 180 degrees around up axis
float angle = 180f * elapsedTimePortion;
Vector3 newForward = Quaternion.AngleAxis(angle, Vector3.up) * startForward;
transform.rotation = Quaternion.LookRotation(newForward);
yield return null;
// next frame - update how long we've been turning
float newElapsedTimePortion = elapsedTimePortion + Time.deltaTime/turnPeriod;
if (newElapsedTimePortion >= 0.5f && elapsedTimePortion < 0.5f)
{
// If we've just passed 50% of the turn,
// make fish move opposite direction
moveDirection = endDirection;
}
elapsedTimePortion = newElapsedTimePortion;
}
// we're done turning, just set the rotation to the end rotation.
isTurning = false;
transform.rotation = Quaternion.LookRotation(-startForward);
// Does removeBeyond need to be updated here? There are a few lines in Start()
// which would suggest that.
}
Have a simple AI thats follows the player when in range and randomly moves the ai around when it's not in the player range. When the AI hits a wall and is out of the players range it starts to spin all the time. Can't work out why it keeps doing so.
I may be missing a simple thing...
Many thanks for any help.
void Update()
{
Target = GameObject.FindGameObjectWithTag("Player");
if (Vector3.Distance(Target.transform.position, transform.position) < 25)
{
followPlayer();
}
else
{
randomMovement();
}
}
public void followPlayer()
{
if (Vector3.Distance(transform.position, Target.transform.position) >= MinDist)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
transform.LookAt(Target.transform);
if (Vector3.Distance(transform.position, Target.transform.position) <= MaxDist)
{
}
}
else
{
}
}
public void randomMovement()
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
transform.Rotate(RandomDirection * Time.deltaTime * 10.0f);
}
void OnCollisionEnter(Collision col)
{
bool hasTurned = false;
if (col.transform.gameObject.name != "Terrain")
{
if(hasTurned == false)
{
RandomDirection = new Vector3(0, Mathf.Sin(TimeBetween) * (RotationRange / 2) + OriginalDirection, 0);
randomMovement();
hasTurned = true;
}
else
{
randomMovement();
hasTurned = false;
}
Debug.Log("Hit");
}
The reason it is continuously spinning is because you are continuously calling randomMovement() in your Update() which continously applies a rotation to your object with Rotate(). It sounds like what you are instead trying to do is have the object wander aimlessly every few seconds. You could do this by implementing on timer on your randomMovement() so that every few seconds, it generates a new rotation(similar to what you have in the onCollision). Example below.
float t = 0;
public void randomMovement()
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
t += Time.deltaTime;
if (t > 3f) // set to a new rotation every 3 seconds.
{
t = 0; // reset timer
RandomDirection = new Vector3(0, Random.Range(0f, 360f), 0); // turn towards random direction
transform.Rotate(RandomDirection);
}
}
I began to create a platformer with Monogame and C# recently and got a strange bug with ground collision. I made block and player, both use bounding collisions like this:
public Rectangle Collision
{
get => new Rectangle((int)_pos.X, (int)_pos.Y, _w, _h);
}
//Only for player entity
public Rectangle GroundCollision
{
get => new Rectangle((int)_pos.X, (int)_pos.Y+_w, _w, 1);
}
It works good, though sometimes space appears space ply and ground like so:
.
Both block and player textures are white, but distance in 1 pix is visible. After some jumps (sometimes with movement) may fix it, though after it began to hover again. Here is player update code:
public void Update()
{
if (_movable)
{
Vector2 oldPos = _pos;
_pos += new Vector2(_vel, -_acc);
if(_pos.Y >= 96)
{
bool c = Collides;
}
if (Grounded)
{
_pos += new Vector2(0, _acc);
_acc = 0;
}
/*if (Collides)
{
_pos = oldPos;
}*/
}
}
//Entity Methods
protected void _Move(float dir, float delta)
{
_vel = (float) _speed * dir * delta;
}
protected void _Gravity(float delta)
{
float fallspeed = (float) Math.Round(Phys.gravity * delta * -5f);
if (_acc - fallspeed >= 0)
_acc = fallspeed;
else if (_acc >= 0)
_acc = 0;
}
protected void _Jump(float delta)
{
_acc += _jumpForce * delta * 10f;
}
So, is there any ways to fix this?
I'm hoping there's someone out there that can help me with a small problem.
Currently I have an Input Manager attached to the main camera to allow the user to pan around the map by moving the mouse to the edges of the window, but I've encountered a slight problem which I've tried to fix myself to no avail.
If the mouse goes outside of the window, the panning still happens, which I find irritating when I'm debugging or using other applications. So I am hoping that someone can help me to stop the movement happening when the mouse is outside the game window.
Here is the code for my Input Manager.
using UnityEngine;
using System.Collections;
public class InputManager : MonoBehaviour {
public Vector3 position = new Vector3(0,0, -10);
public int boundary = 50;
public int speed = 4;
private int screenBoundsWidth;
private int screenBoundsHeight;
// Use this for initialization
void Start()
{
screenBoundsWidth = Screen.width;
screenBoundsHeight = Screen.height;
}
// Update is called once per frame
void Update()
{
if (Input.mousePosition.x > screenBoundsWidth - boundary) {
position.x += speed * Time.deltaTime;
}
if (Input.mousePosition.x < 0 + boundary) {
position.x -= speed * Time.deltaTime;
}
if (Input.mousePosition.y > screenBoundsHeight - 10) {
position.y += speed * Time.deltaTime;
}
if (Input.mousePosition.y < 0 + boundary) {
position.y -= speed * Time.deltaTime;
}
Camera.mainCamera.transform.position = position;
}
}
Thank you for your time.
EDIT
I have come up with a hacky work around, but it still causes the movement to happen in certain locations around the outside of the window. I am hoping someone can come up with a better solution.
if (Input.mousePosition.x < screenBoundsWidth && Input.mousePosition.y < screenBoundsHeight) {
if (Input.mousePosition.x > screenBoundsWidth - boundary) {
position.x += speed * Time.deltaTime;
}
}
if (Input.mousePosition.x > 0 && Input.mousePosition.y > 0) {
if (Input.mousePosition.x < 0 + boundary) {
position.x -= speed * Time.deltaTime;
}
}
if (Input.mousePosition.y < screenBoundsHeight && Input.mousePosition.x < screenBoundsWidth) {
if (Input.mousePosition.y > screenBoundsHeight - 22) {
position.y += speed * Time.deltaTime;
}
}
if (Input.mousePosition.y > 0 && Input.mousePosition.x > 0) {
if (Input.mousePosition.y < 0 + boundary) {
position.y -= speed * Time.deltaTime;
}
}
3 Ideas:
Rect screenRect = new Rect(0,0, Screen.width, Screen.height);
if (!screenRect.Contains(Input.mousePosition))
return;
The same can be written more verbously as:
float mouseX = Input.MousePosition.x;
float mouseY = Input.MousePosition.y;
float screenX = Screen.width;
float screenY = Screen.height;
if (mouseX < 0 || mouseX > screenX || mouseY < 0 || mouseY > screenY)
return;
// your Update body
...which is pretty much the same as your "hacky" solution (which is completely valid imho).
Another option is to create 4 Rect objects for each screen border, then check if mouse is inside those rects. Example:
public float boundary = 50;
public float speed = 4;
private Rect bottomBorder;
private Rect topBorder;
private Transform cameraTransform;
private void Start()
{
cameraTransform = Camera.mainCamera.transform
bottomBorder = new Rect(0, 0, Screen.width, boundary);
topBorder = new Rect(0, Screen.height - boundary, Screen.width, boundary);
}
private void Update()
{
if (topBorder.Contains(Input.mousePosition))
{
position.y += speed * Time.deltaTime;
}
if (bottomBorder.Contains(Input.mousePosition))
{
position.y -= speed * Time.deltaTime;
}
cameraTransform.position = position;
}
The tricky part here is that Rect coordinates have Y axis pointing down and Input.mousePosition has Y pointing up... so bottomBorder Rect has to be on the top, and topBorder has to be at the bottom. Left and right borders are not affected.
Due to the way Unity and the various host operating systems interact, you have limited control of the mouse cursor. (in short the OS controls the mouse Unity just reads it) That being said you do have some options. Screen.lockCursor jumps to mind.
http://docs.unity3d.com/Documentation/ScriptReference/Screen-lockCursor.html
It won't do exactly what you are looking for but it might be a good starting point
Time.speed = 0;
is this what you want?
I'm trying to get my enemies to follow the player and stop within 20 pixels, I have tried a number of algorithms including the Vector2.Lerp(); method to try and fix this but it keeps breaking the build. Any help would be greatly appreciated. The code is below.
public void Update(GameTime gameTime)
{
if (this.IsAlive)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
double distanceToPlayer = Math.Sqrt(Math.Pow(Level.Player.Position.X - this.Position.X, 2) + Math.Pow(Level.Player.Position.Y - this.Position.Y, 2));
// Calculate tile position based on the side we are walking towards.
float posX = Position.X + localBounds.Width / 2 * (int)direction;
int tileX = (int)Math.Floor(posX / Tile.Width) - (int)direction;
int tileY = (int)Math.Floor(Position.Y / Tile.Height);
if (waitTime > 0)
{
// Wait for some amount of time.
waitTime = Math.Max(0.0f, waitTime - (float)gameTime.ElapsedGameTime.TotalSeconds);
if (waitTime <= 0.0f)
{
// Then turn around.
direction = (FaceDirection)(-(int)direction);
}
}
else
{
// If we are about to run into a wall or off a cliff, start waiting.
if (Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Impassable || Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Passable) //is the enemy is close and is not attacking, attack and turn!
{
waitTime = MaxWaitTime;
}
else
{
// Move in the current direction.
Vector2 velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
position = position + velocity;
}
}
dtAttack += gameTime.ElapsedGameTime;
AttackPlayer();
}
else
{
dt += gameTime.ElapsedGameTime;
if (dt.TotalSeconds > (sprite.Animation.FrameCount * sprite.Animation.FrameTime))
this.Remove = true;
}
}
Does it have to be 20 pixels on the screen? Seems strange.
You could try to calculate the euclidean distance between the player and the enemy, using the Vector2.Distance method. If the distance is 20 or lower, stop the enemy. If not, keep following the player.