I've been having some trouble applying correct velocity changes to my ball when it hits bricks in my Breakout clone. In a previous question, I was advised to use continuous collision detection, as well as other methods such as finding the intersection between the ball and the brick when it hits a corner to determine which direction the ball should reflect. I've applied this to my code below, but there are still occasions when the ball will just completely plow through a collection of bricks. This is more noticeable when it hits moving bricks.
In Level.cs Update method:
Bricks.ForEach(brick => Balls.ForEach(ball => ball.Collide(brick)));
In Ball.cs:
public bool Touching(Brick brick)
{
var position = Position + (Velocity * Speed);
return position.Y + Size.Y >= brick.Position.Y &&
position.Y <= brick.Position.Y + brick.Size.Y &&
position.X + Size.X >= brick.Position.X &&
position.X <= brick.Position.X + brick.Size.X && brick.Health > 0 && brick.Lifespan == 1F;
}
public void Collide(Brick brick)
{
if (!Touching(brick)) return;
var position = Position + (Velocity * Speed);
var bounds = new Rectangle((int)position.X, (int)position.Y, Texture.Width, Texture.Height);
var nonCCDBounds = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
{
var change = new Vector2(Velocity.X, -Velocity.Y);
if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
{
var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
if (intersection.Width < intersection.Height || nonCCDIntersection.Width < nonCCDIntersection.Height){
change = new Vector2(-Velocity.X, Velocity.Y);
}
}
if (bounds.Intersects(brick.Top))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y - 1)) != null)
change = new Vector2(-Velocity.X, Velocity.Y);
else if ((Position - Velocity).Y > brick.Position.Y)
change = new Vector2(-Velocity.X, Velocity.Y);
}
if (bounds.Intersects(brick.Bottom))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y + 1)) != null)
change = new Vector2(-Velocity.X, Velocity.Y);
else if ((Position - Velocity).Y < brick.Position.Y + brick.Texture.Bounds.Height)
change = new Vector2(-Velocity.X, Velocity.Y);
}
ReflectBall(brick, change);
return;
}
if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
{
var change = new Vector2(-Velocity.X, Velocity.Y);
if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
{
var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
if (intersection.Width > intersection.Height || nonCCDIntersection.Width > nonCCDIntersection.Height)
{
change = new Vector2(Velocity.X, -Velocity.Y);
}
}
if (bounds.Intersects(brick.Left))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X - 1, brick.GridPosition.Y)) != null)
change = new Vector2(Velocity.X, -Velocity.Y);
else if ((Position - Velocity).X > brick.Position.X)
change = new Vector2(Velocity.X, -Velocity.Y);
}
if (bounds.Intersects(brick.Right))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X + 1, brick.GridPosition.Y)) != null)
change = new Vector2(Velocity.X, -Velocity.Y);
else if ((Position - Velocity).X < brick.Position.X + brick.Texture.Bounds.Width)
change = new Vector2(Velocity.X, -Velocity.Y);
}
ReflectBall(brick, change);
}
}
public void ReflectBall(Brick brick, Vector2 reflection)
{
Position = Position - Velocity;
Velocity = reflection;
if (brick.Health < 9)
{
brick.Health--;
brick.Life --;
}
if (brick.Health > 0 && brick.Life > 0)
{
brick.Texture = Assets.GetBrick(brick.TextureName, brick.Health);
}
}
It's a bit of a mess but it's the closest I've got to having decent collision. It would be much easier if there was a fast way of finding out collision points and applying correct velocity changes.
Related
So basically what i am trying to do is create an animation script that does not just take simple direction like Up(0,1),Down(0,-1),Left(-1,0),Right(1,0)(Those numbers being vectors) but to have range of directions for to fill in the gap between those directions and play the more dominant direction Up,Down,Left,Right.
So each quarter section below to be set to an animation.
I have created a few scripts to try and achieve this i came up with one that wasn't perfect but it did work when dragging the enemies around in the scene editor while the game was running. When using their movements scripts for their movement it did not work. The animation script is below and underneath that how i move the enemies.
Below would be how i calculate the direction the enemy was moving:
IEnumerator directionFinder()
{
// while (true)
//{
Vector3 previousPosition = transform.position;
yield return new WaitForSeconds(1/10);
currentPosition = transform.position;
currentPosition.x = currentPosition.x - previousPosition.x;
currentPosition.y = currentPosition.y - previousPosition.y;
currentPosition.z = 0f;
angle = (Mathf.Atan2(currentPosition.y, currentPosition.x) * Mathf.Rad2Deg);
if(angle < 0)
{
angle = Mathf.Abs(angle) + 180;
}
// }
}
Then this is what calculated which animation to use:
void animationDirection()
{
if (angle == 0)
{
anim.SetInteger("Move", currentAnim);
}
if (angle > 45 && angle <135)//W
{
anim.SetInteger("Move", 1);
currentAnim = 1;
}
else if (angle > 135 && angle < 225)//A
{
anim.SetInteger("Move", 2);
currentAnim = 2;
}
else if (angle > 225 && angle < 315)//S
{
anim.SetInteger("Move", 3);
currentAnim = 3;
}
else if((angle < 45 && angle >= 0) || (angle > 315 && angle <= 0))//D
{
anim.SetInteger("Move", 4);
currentAnim = 4;
}
}
This using the code below it would move to the waypoint that was selected via the rest of my code that would just loop through a set of waypoints changing the waypoint when it arrived at one.
transform.position = Vector2.MoveTowards(transform.position, waypoints[waypointNUmber].transform.position, speed * Time.deltaTime);
Below I tried another way but it did not work and created weird buggy animations:
IEnumerator directionalAnimation()
{
while (true)
{
Debug.Log("working");
Vector2 previousPosition = transform.position;
yield return new WaitForSeconds(1/10);
Vector2 currentPosition = transform.position;
Vector2 direction = (previousPosition - currentPosition).normalized;
float moveX = direction.x;
float moveY = direction.y;
if (moveX < 0)
{
moveXNegative = true;
moveX = Mathf.Abs(moveX);
}
if (moveY < 0)
{
moveYNegative = true;
moveY = Mathf.Abs(moveY);
}
if (direction.magnitude != 0)
{
if (moveX > moveY)
{
if (moveXNegative)
{
//left
Walking = true;
anim.SetInteger("Move", 2);
moveYNegative = false;
}
else
{
//Right
Walking = true;
anim.SetInteger("Move", 4);
moveYNegative = false;
}
}
else if (moveY > moveX)
{
if (moveYNegative)
{
//Down
Walking = true;
anim.SetInteger("Move", 3);
}
else
{
//Up
Walking = true;
anim.SetInteger("Move", 1);
}
}
}
else
{
source.Pause();
Walking = false;
}
if (Walking)
{
source.UnPause();
}
}
}
So, I'm generating a Vector2 using Unity's Random.Range, this is basically the x and y value of the direction the mobile should be moving.
m_CurrentMovementInput = new Vector2
(Random.Range(-0.75f, 0.75f), Random.Range(-0.75f, 0.75f));
The problem is the mobiles are only moving in diagonal angles. Which makes it seem like when it produces the random it's producing like..
(-0.35f, -0.35f) or (0.35f, -0.35f) or (-0.35f, 0.35f)
Which makes little sense to me as I understand the random function.
I'm sure I can get the desired behaviour by only changing one at a time, but I'd still like to understand its current behavior.
Edit: I believe this is all the relevant code. Sorry it took so long had to go get it from the repo.
void ProcessMovement()
{
ModifyComputerMovement(m_CurrentMovementInput.x, m_CurrentMovementInput.x);
}
float ModifyMovementInput(float input)
{
return input * m_MovementSpeedBase;
}
void RandomizeMovementInput()
{
m_CurrentMovementInput = new Vector2
(Random.Range(-0.75f, 0.75f), Random.Range(-0.75f, 0.75f));
if(RandomBoolean)
{
m_CurrentMovementInput += new Vector2
(Random.Range(-0.05f, 0.05f), Random.Range(-0.05f, 0.05f));
}
}
void ModifyComputerMovement(float x, float y)
{
m_VerticalInput = x;
m_HorizontalInput = y;
if ((m_VerticalInput == 0 && m_HorizontalInput == 0))
{
m_IsMoving = false;
}
else { m_IsMoving = true; }
UpdateCpuLocation();
}
bool m_ShouldRun;
bool m_ShouldSprint;
void UpdateCpuLocation()
{
if (!IsMoving())
m_CurrentMoveType = MovementType.None;
else
m_CurrentMoveType = MovementType.Walk;
m_DeltaVector = new Vector3
(
ModifyMovementInput(m_VerticalInput),
m_IsJumping ? ComputeJumpVelocity() : ApplyGravity(),
ModifyMovementInput(m_HorizontalInput)
);
m_GammaVector =
transform.forward * m_DeltaVector.x + transform.right * m_DeltaVector.z;
m_DeltaVector = new Vector3
(m_GammaVector.x, m_DeltaVector.y, m_GammaVector.z);
if (m_ShouldRun && IsMoving())
{
if (m_Controller.isGrounded)
{
m_CurrentMoveType = MovementType.Run;
m_DeltaVector *= m_RunSprintFactor;
if (m_ShouldSprint && IsMoving())
{
m_CurrentMoveType = MovementType.Sprint;
m_DeltaVector *= m_RunSprintFactor;
}
}
}
m_VectorShift = !m_Controller.isGrounded ?
(m_DeltaVector * Time.deltaTime) * m_JumpModifier : (m_DeltaVector * Time.deltaTime);
if (m_Controller.isGrounded)
m_VectorShift *= (m_PlayerState.GetCurrentEnergyLevelRatio());
m_VectorShift = new Vector3
(
m_VectorShift.x,
m_VectorShift.y,
m_VectorShift.z
);
#region Debug
//Debug.Log("Vector Shift: " + m_VectorShift);
//Debug.Log("Vector Shift Breakdown:");
/*Debug.Log
(String.Format
("VectorShift (X): {0} | VectorShift (Y): {1} | VectorShift (Z): {2}",
(double)m_VectorShift.x, (double)m_VectorShift.y, (double)m_VectorShift.z));*/
#endregion
if (m_PlayerState.GetCurrentEnergyLevelRatio() < m_MinimumEnergyThreshold)
m_VectorShift *= m_LowStamMoveModulus;
//m_MoveEventArg =
//new PlayerMoveActionEventArgs(m_VectorShift, GetCurrentMovementType);
if (m_IsMoving)
{
//EventDispatcher.InvokeMoveAction(m_MoveEventArg);
}
m_VectorShift = m_VectorShift * (Time.deltaTime * m_TimeWarpModifier);
m_Controller.Move(m_VectorShift);
}
Edit, Part II
So, the only thing that is really different is this:
void QueryPlayerInput()
{
m_VerticalInput = Input.GetAxis("Vertical");
m_HorizontalInput = Input.GetAxis("Horizontal");
if ((m_VerticalInput == 0 && m_HorizontalInput == 0))
{
m_IsMoving = false;
}
else { m_IsMoving = true; }
UpdatePlayerLocation();
}
as the title says! I'm working on a sort of Civilization type city builder game as practice for the coming school year (only a second year video game programming student!).
I've already gotten a grid generated in game, which looks like:
This!
As you can see I've already gotten a rudimentary selection system set up, wherein currently I can only select one tile at a time until I deselect it, then I can select a new tile. The tiles are selected using an OnClick function tied to a collider on the prefab. (will include my code I have currently at the end!)
What I'm wondering how to do is have a tile deselect automatically whenever I select a new tile, so I have only one tile selected at a time.
This is what I have for now for selection.
public void OnMouseDown() {
if (GameManager.Instance.tileSelected == false) {
if (enabled == false) {
tileOutlineSprite.SetActive (true);
enabled = true;
GameManager.Instance.tileSelected = true;
this.tileInfo.text = tileType;
}
}
else if (enabled == true) {
tileOutlineSprite.SetActive (false);
enabled = false;
GameManager.Instance.tileSelected = false;
this.tileInfo.text = " ";
}
}
And this is what I'm currently using to generate my grid! I know it might be a lil messy for now, I'm planning on cleaning it up and refining it as I go on!
void generateMap() {
map = new List<List<TileSelect>>(); //generatign the playing field, making a grid of tile prefabs, and storing their positiosn in a 2d list
for (int i = 0; i < mapSizeX; i++) {
List <TileSelect> row = new List<TileSelect>();
for (int j = 0; j < mapSizeY; j++) {
if (i == 0) {
iDiff = 0.8f;
}
if (j % 2 == 0) {
iDiff = i + (.2f * (i+1));
} else if (i != 0) {
iDiff = i + 0.6f + (.2f * (i+1));
}
jDiff = j + (.04f * j);
int rand = Random.Range (1, 101);
if (rand <= 45) {
TileSelect tile = ((GameObject)Instantiate (HeavyForestTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Heavy Forest";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 45 && rand <= 70) {
TileSelect tile = ((GameObject)Instantiate (LightForestTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Light Forest";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 70 && rand <= 90 ) {
TileSelect tile = ((GameObject)Instantiate (GrassTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Grassland";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 90 && rand <= 97) {
TileSelect tile = ((GameObject)Instantiate (GrassRockTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Light Rocks";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 97 && rand <= 100) {
TileSelect tile = ((GameObject)Instantiate (GrassRock2Tile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Heavy Rocks";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
}
}
map.Add(row);
}
Oh! and the game is in 2d if that matters for a solution! Let me know if you need anymore info, I'll happily supply it!
A simple way would be to add an additional member to your game manager class:
public class GameManager
{
TileSelect _selectedTile;
public TileSelect selectedTile
{
get { return _selectedTile; }
set
{
//unhighlight the previous selected tile
_selectedTile = value;
//highlight the newly selected tile
}
}
...
}
Include a setter such that everytime you change the selected tile, it unhighlights the selected tile and highlights the new selected tile.
Simply change the selected tile when it is clicked:
void onClick(...)
{
...
//on raycast hit with the 2d tile (targetTile)
gameManager.selectedTile = targetTile;
}
I'm trying to add a bounce effect to an element of a game I'm making using C#. I can't seem to find the correct easing equation for this. Here's what I'm using for the time being:
t.position += (destination-t.position)*0.05f;
if((destination-t.position).magnitude <= 0.01f)
{
t.position = destination;
}
Can anyone help me with changing it to a bouncing equation?
if(setUpEase)
{
// Set the destination of each boardspace
if(!oneTime)
{
beginning = new Vector3(t.position.x, t.position.y, t.position.z);
destination = new Vector3(t.position.x, t.position.y-10, t.position.z);
change = new Vector3(0, -10, 0);
setUpDuration = Random.Range (1.0f, 3.0f);
oneTime = true;
}
// BOUNCING
currentTime += Time.deltaTime;
float t2 = currentTime/setUpDuration;
if(currentTime < setUpDuration)
{
if (t2 < (1/2.75f))
{
t.position = change*(7.5625f*t2*t2) + beginning;
}
else if (t2 < (2/2.75f))
{
t.position = change*(7.5625f*(t2-=(1.5f/2.75f))*t2 + 0.75f) + beginning;
}
else if (t2 < (2.5f/2.75f))
{
t.position = change*(7.5625f*(t2-=(2.25f/2.75f))*t2 + 0.9375f) + beginning;
}
else
{
t.position = change*(7.5625f*(t2-=(2.625f/2.75f))*t2 + 0.984375f) + beginning;
}
}
else
{
t.position = destination;
if(boardId < 113 && boardId > 92)
pie.setUpEase2 = true;
setUpEase=false;
}
}
i have problem with blocking movement i already read question on Blocking Movement On Collision
but i dont have any idea for my own problem.
if you can give me the logic i will try to solve my own problem.
i hope you can help me
this my player class update
:EDIT: thanks for Kai Hartmann for reply
my problem is for 2D graphic and i want to know how to stop movement when object1 collision with object2
public void Update(GameTime gameTime)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Up;
velocity.Y = -3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Down;
velocity.Y = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Right;
velocity.X = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
currentFace = FacePosition.Left;
velocity.X = -3;
}
else
{
//currentFace = FacePosition.Down;
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
and this my collision
public void IntersectWithScore(Vector2 vector1, Vector2 vector2, Texture2D object1, Texture2D object2, int doSomethingToScore, bool isIncrease)
{
if (vector1.X + object1.Width < vector2.X || vector1.X > vector2.X + object2.Width ||
vector1.Y + object1.Height < vector2.Y || vector1.Y > vector2.Y + object2.Height)
{
}
else
{
player1.Velocity = Vector2.Zero;
}
}
No need for a collision check Method as far as I'm concerned :) I've written this code really quickly but should work okay?(ish). I've assumed you want to check if something is in the player's way and also assumed that there are more than one objects that could be in your players way so to do this I suggest making a class for the 'Solid' object and giving it a position, width and height. Then make a list of this class to contain the multiple 'solid' objects.
// collidable object's Class name = SolidBlock
SolidBlock newSolidBlock;
List<SolidBlock> solidBlocksList = new List<SolidBlock>();
// This will create 10 'solid' objects and adds them all to a list
// This would preferably go in the Load() function so you can set the positions of
// the blocks after creating them (after the loop)
int i = 0;
while (i < 10)
{
newSolidBlock = new SolidBlock(Vector2, position) // Assuming width and height
// are set within class
solidBlocksList.Add(newSolidBlock);
i++;
}
// It doesn't matter how you create them, the important lines here are the creation of a
// newSolidBlock and then the line adding it to the list of blocks.
solidBlocksList[0].position = new Vector (20, 50); // < for example this is how you would set
// a block's position.
Then in your players update function you refer to this list of objects.
// Input a list of the objects you want your player to collide with
// this is a list in case you have multiple object2s you want to check
// for collision.
// An Object2 should have a position, height, and width, yes?
public void Update(GameTime gameTime, List<Object2> object2List)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y <= o.position.Y + o.height)
{
velocity.Y = 0;
}
else
{
velocity.Y = -3;
}
}
currentFace = FacePosition.Up;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y + playerWidth >= o.position.Y)
{
velocity.Y = 0;
}
else
{
velocity.Y = 3;
}
}
currentFace = FacePosition.Down;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.X + playerWidth >= o.position.X)
{
velocity.X = 0;
}
else
{
velocity.X = 3;
}
}
currentFace = FacePosition.Right;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
foreach (Object2 o in object2List)
{
if (position.X <= o.position.X + o.width)
{
velocity.X = 0;
}
else
{
velocity.X = -3;
}
}
currentFace = FacePosition.Left;
}
else
{
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
Sorry if this isn't much help, especially the first block of code, that was very much an example of how you could create multiple object2s.