I have created two Rectangles, you can move and jump with one of them, and the other one is standing still on the Form as an obstacle.
I want the obstacle to work as an obstacle(or a wall if you will), basically I want the movable rectangle to stop when its right side is colliding with the obstacle's left side(and so forth).
I found this code how to NOT detect collision(because it's apparently easier to not detect collision)between two rectangles in an article:
OutsideBottom = Rect1.Bottom < Rect2.Top
OutsideTop = Rect1.Top > Rect2.Bottom
OutsideLeft = Rect1.Left > Rect2.Right
OutsideRight = Rect1.Right < Rect2.Left
//or
return NOT (
(Rect1.Bottom < Rect2.Top) OR
(Rect1.Top > Rect2.Bottom) OR
(Rect1.Left > Rect2.Right) OR
(Rect1.Right < Rect2.Left) )
But I'm not sure how to implement it. I have a bool called "player1.left" which becomes true when I press 'A' on the keyboard('D' to move to the right, and 'W' to jump)which when true will move the rectangle 10 pixels to the left(in the Timer_Tick event).
EDIT:
"rect1.IntersectsWith(rect2)" works to detect collision. But how would I use that(what should go inside the if-statement) if I want the movable rectangle to stop moving to the right(but still be able to jump and move to the left)if its right side collides with the obstacle's left side(and so forth)?
//UPDATE
Let's say you have the class PlayableCharacter which inherits from Rectangle.
public class PlayableCharacter:Rectangle {
//position in a cartesian space
private int _cartesianPositionX;
private int _cartesianPositionY;
//attributes of a rectangle
private int _characterWidth;
private int _characterHeight;
private bool _stopMoving=false;
public PlayableCharacter(int x, int y, int width, int height)
{
this._cartesianPositionX=x;
this._cartesianPositionY=y;
this._chacterWidth=width;
this._characterHeight=height;
}
public bool DetectCollision(PlayableCharacter pc, PlayableCharacter obstacle)
{
// this a test in your method
int x=10;
if (pc.IntersectsWith(obstacle)){
Console.Writeline("The rectangles touched");
_stopMoving=true;
ChangeMovingDirection(x);
StopMoving(x);
}
}
private void ChangeMovingDirection(int x)
{
x*=-1;
cartesianPositionX+=x;
}
private void StopMoving(int x)
{
x=0;
cartesianPositionX+=x;
}
}
In the code I`ve given you, in a situation when the character is going to the right, the x value which is positive, the character will face the other direction. If he moves on the left, if he collides with an obstacle, he will then face the other direction.
With StopMoving, even though you make a script which runs over time in a loop for example, it will never make the character move.
I think this should give the basis for your work. If there are any issues, please comment on the solution I wrote and I'll do my best to help you out if it's in my reach.
Related
Still pretty new to Unity and C#. I can usually find my way around when it comes to setting/changing the position of an object, but when it comes to rotation it's still mind boggling to me, with quaternion, eulerangles etc..
I'm trying to check if an object (a head) is facing a certain direction, if it's not facing that direction it needs to set it to that direction depending which way it goes (up, down, left or right). This is for a snake game (like the old top down nokia game) I'm currently developing.
I tried to look it up, but none of the answers are specific to what I'm trying to do.
As of right now this is what I have for player input (a snippet of code):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left)
{
snakeHeadRotation.transform.Rotate(0, 180, 0); // Keeps rotating when left is pressed
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As you can see the snakeHeadRotation.transform.Rotate(0, 180, 0); is getting called each time the player presses the left key for instance, thus turning the head of the snake even when the snake is still traveling to the left.
My logic would be to store the rotation value somewhere (type Quaternion/bool maybe) and check to see if that rotation value matches up with the current rotation value set to left (which is snakeHeadRotation.transform.Rotate(0, 180, 0);) If that's the case, don't do anything. If it isn't, set it to snakeHeadRotation.transform.Rotate(0, 180, 0); for instance.
the code would look something like (writing it out):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left && rotation is not equal to snakeHeadRotation.transform.Rotate(0, 180, 0))
{
snakeHeadRotation.transform.Rotate(0, 180, 0);
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As said, I'm very unfamiliar with setting or storing rotation values. How can I achieve this? Is this even the right approach or is there a more logical one? I can't figure this one out myself..
I hope I explained it right, I'm usually terrible at explaining things.
Using Transform.Rotate(x) will rotate your head by x degrees relatively to your current head direction.
What you want I suppose is to set the absolute rotation of your head depending on the arrow key you pressed. Thus, even if you press multiple times the same arrow key, the head will stay in the correct direction.
To do that you can create 4 rotations for each head direction and so use them as needed.
For example, if your head is facing forward by default:
// create your directions
private static Quaternion forwardDirection = Quaternion.Identity;
private static Quaternion rightDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.right);
private static Quaternion leftDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.left);
private static Quaternion backDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.back);
...
private int GetAxisRaw(Axis axis)
{
...
if (left)
{
// set the absolute direction of your head to the left
snakeHeadRotation.transform.rotation = leftDirection;
return -1;
}
if (right)
{
// set the absolute direction of your head to the right
snakeHeadRotation.transform.rotation = rightDirection;
return 1;
}
...
}
I would just create 4 quaternions for each direction and set the rotation according to the key pressed (so like hear.rotation = leftQ). This is the simplest way.
I am away from my PC currently but I will try to find another solution when I can
You could compare the current rotation to the known rotations for the 4 directions.
But don't go that way, it's bad practice for several reasons:
what if later you want to add animations, e.g. the head turns in .5 seconds, not instantly?
comparing 4 floats only to check one of 4 possible directions is inefficient.
Create a enum instead:
public enum Direction
{
Up,
Down,
Left,
Right
}
Add a member of this type to your behavior, to keep track of where your snake head is going.
Something like:
Direction direction;
...
private int GetAxisRaw(Axis axis)
{
switch (direction)
{
case Direction.Up:
if (left && !right)
direction = Direction.Left;
... turn snake left ...
else if (right && !left)
direction = Direction.Right;
... turn snake right ...
// ignore inputs for up or down if going up
break;
... repeat for the other directions and possible inputs ...
}
...
}
I am trying to achieve a very basic 2D game right now where an enemy is on the screen and it bounces back and forth between set points (50 and 500) kind of like a space invaders sort of thing. My issue is I can only get it to go right, but then not come back towards the left and repeat.
I was messing around with coding it myself before bothering to look into it and actually figure it out but I thought I had something that would work, but well it doesn't, my issue is I don' get why.
My code is supposed to work like a switch, two if statements within the Update loop, one comes on the other goes off, one moves it right the other moves it left, I thought that was fine but it doesn't do that. It moves it right just fine but then the left part just doesn't work.
So, why does the following code not work?
namespace _2D_game_num1
{
class Enemy
{
int health;
Vector2 enemy_location = new Vector2(50, 50);
Vector2 enemy_speed = new Vector2(1, 1);
Player player = new Player("dummy");
public Enemy()
{
health = 100;
}
public void UpdateLocation()
{
//Vector2 player_pos = player.GetLocation();
//if (player_pos.X < 200)
// Using the players location to figure out where the enemy should move
bool right = true;
bool left = false;
if (right)
{
enemy_location.X += enemy_speed.X;
if (enemy_location.X == 500)
{
right = false;
left = true;
}
}
if (left)
{
enemy_location.X -= enemy_speed.X;
if (enemy_location.X == 50)
{
right = true;
left = false;
}
}
}
public Vector2 GetLocation()
{
return enemy_location;
}
}
}
And then in the main Game class I have it so enemy1.UpdateLocation(); is within the Update section correctly (along with my players movement which works fine).
Try this:
public void UpdateLocation()
{
enemy_location.X += enemy_speed.X;
if (enemy_location.X <= 50 || enemy_location.X >= 500)
{
enemy_speed.X = new Vector2(-enemy_speed.X, enemy_speed.Y);
}
}
What we are doing here is moving the enemy based on it's current speed. Then if the location is on the left or right of the screen, change direction.
Sometimes it pays to keep things simple. Get rid of the left and right flags, as they are just confusing things. When you're dealing with Update methods you're typically changing the state of something. The way you had it before, the left and right state was getting reset every time UpdateLocation is called.
Btw, you might want to consider passing in GameTime to your Update methods. Typically, when you're moving things around in real-time you'll want to multiply movement by some kind of deltaTime to keep things smooth on all devices. You're probably getting away with it because by default it'll be a fixed frame rate, but that may not always be the case, so it's a good habit to get into.
I have this problem, that I want to be able to click on a "tile" on the screen and then a pop-up menu should be shown just next to the tile. I can click on the tile and then a pop-up menu shows up but not where I want it.
On the picture here I've clicked on the top left one.
My code for placing the picture is as following:
using UnityEngine;
using System.Collections;
public class TowerMenu : MonoBehaviour
{
bool showMenu = false;
float x;
float y;
GUIStyle myStyle;
public Texture2D[] towers;
void OnGUI()
{
if(showMenu)
{
//Bear tower
GUI.Button(new Rect(x + 10, y - 25, 50, 50), towers[0]);
//Seal tower
GUI.Button(new Rect(x + 10, y + 25, 50, 50), towers[1]);
}
}
public void ShowMenu(Vector2 pos)
{
showMenu = true;
x = pos.x;
y = pos.y;
}
}
Hope anyone can help me :)
Sry, i cant comment because i dont have enough rep, this is a comment to Steven Mills answer and comments to that post.
The first error comes because you are calling WorldToViewportPoint as if it was a static member function which it isnt(Think of if you had 2 Cameras you would have to specify which Camera you want, reading up on what a static member is would be helpful here). What you need to do to fix this is get a reference of your MainCamera and call the function from that instance(best method would probably be with a public variable and dragging the camera on the script in the editor)
The second error occurs because you are trying to give a Vector3 to ShowMenu() which requires a Vector2. The third is probably a product of the compiler trying to fix error 2
This is a logical error because the tiles are GameObjects and thus the transform.position are positions in 3d space. What you actually want is the 2d-pixel-position on the screen of your MainCamera. To get this you need to call WorldToScreenPoint instead of WorldToViewportPoint. Sadly, as you will notice you will also get a Vector3 here which is not what you want, but you can just take the x and y coordinates as your pixel screen coordinates. the z-coordinate denotes the distance from the camera.
Hope that clears it up a little instead of confusing you ;D
Feel free to ask again, but also try to read the Unity Script Reference and try to understand what is written there and what it means :)
By the look of it, your ShowMenu method is receiving a pos of (0,0), which is why the two buttons are placed at what seems to be a position of (10,-25) and (10,25) respectively.
Without seeing the code calling ShowMenu I can't be sure what location you're giving, but my guess would be that the tiles belong to a parent object, and you're passing the local position instead of the world position.
If you post the code in which you call ShowMenu I may be able to point out any problems.
EDIT:
Based on the information provided in the comments, the problem is that the position needs converting between the world coordinates and screen coordinates:
void OnMouseDown()
{
if(state == State.water)
{
errorHandler.sendError("You can't click on that");
}
if(state == State.ice)
{
towerMenu.ShowMenu(camera.WorldToViewportPoint(this.transform.position));
}
}
and change the ShowMenu to this:
public void ShowMenu(Vector3 pos)
{
showMenu = true;
x = pos.x;
y = pos.y;
}
I'm currently writing a platforming game in XNA.
My collision testing currently operates by testing the bounding box of the player with every tile's bounding box by running through a foreach loop. However, I can't figure out how to test whether the player is NOT touching any blocks.
How would I run through my array and test for if they player isn't touching any blocks so that I can check if he is in mid air? Any help or advice would be greatly appreciated. Thank you in advance!
/////////////////////////////////////
My collision code is
if (personRectangle.TouchTopOf(newRectangle))
{
onGround = true;
test = false;
test2 = true;
}
The corresponding bool test is
public static bool TouchTopOf(this Rectangle r1, Rectangle r2)
{
return (r1.Bottom >= r2.Top - 1 &&
r1.Bottom <= r2.Top + (r2.Height / 2) &&
r1.Right >= r2.Left + (r2.Width / 5) &&
r1.Left <= r2.Right - (r2.Width / 5));
}
And this piece of code tests collision in the Game1.cs update.
foreach (CollisionTiles tile in map.CollisionTiles)
player.Collision(tile.Rectangle);
Which is a list of tiles in my map class
private List<CollisionTiles> collisionTiles = new List<CollisionTiles>();
XNA Rectangles have a built-in intersects method:
if (rect1.Intersects(rect2)){
...
}
just loop through the tile rectangles and call intersects on each one. To make it more efficient you should map the player coords onto world coords and determine the tiles nearest to the player, doing this you can narrow down the number of tiles you need to loop over.
EDIT: for clarification, you could have:
bool collision=false;
foreach (CollisionTiles tile in map.CollisionTiles) {
if(personRectangle.Intersects(tile.Rectangle)) {
collision =true;
break;
}
}
Then if collision is true you know there was a collision somewhere.
Unless you have a reason not to, keep it simple. Just keep track of how many things you collided with. If your count ends up being 0 then you didn't collide with anything.
Without worrying about modifying / optimizing any of the code you have, it could be something like:
int touchedCount = 0;
foreach (CollisionTiles tile in map.CollisionTiles)
{
if (player.Collision(tile.Rectangle))
{
touchedCount++;
}
}
if (touchedCount == 0)
{
//You did not collide with anything
}
Then inside your collision method do something similar to keep track of your checks and if you collided at all. This would probably be easiest using Rectangle.Intersects as the other answer suggests.
I'm a starter in XNA and I'm trying to make a pong-game.
I've been able to make a pong game, but all the code was in one class. So I wanted to try to add a little bit of OOP and I've made a class for the ball and one for the pad.
The ball moves perfectly, but I don't seem to able to make the ball bounce from the pads.
These are the codes I use:
To move the pads
Game1.cs
#region left
if (_KBS.IsKeyDown(Keys.W) || _KBS.IsKeyDown(Keys.Z))
Left.MoveUp();
else if (_KBS.IsKeyDown(Keys.S))
Left.MoveDown();
#endregion
#region right
if (_KBS.IsKeyDown(Keys.Up))
Right.MoveUp();
else if (_KBS.IsKeyDown(Keys.Down))
Right.MoveDown();
#endregion
pad.cs
public void MoveUp() {
if (!paused)
RecPad.Offset(0, -speed);
CheckBorders();
}
public void MoveDown() {
if (!paused)
RecPad.Offset(0, speed);
CheckBorders();
}
private void CheckBorders() {
MathHelper.Clamp(recPad.Y, borders.Top, borders.Bottom - recPad.Height);
}
To check if the ball bounces
ball.cs
public void CheckBounce() {
if ((myBounds.Intersects(left) && movement.X < 0) || (myBounds.Intersects(right) && movement.X > 0))
movement.X *= -1;
}
public void Draw(SpriteBatch sBatch, Texture2D texture, Color color, Rectangle left, Rectangle right) {
this.left = left;
this.right = right;
Move();
sBatch.Begin();
sBatch.Draw(texture, myBounds, color);
sBatch.End();
}
pad.cs
public Rectangle RecPad {
get { return recPad; }
private set { recPad = value; }
}
Game1.cs
Ball.Draw(spriteBatch, ball, Color.White, Left.RecPad, Right.RecPad);
I seemed to get the pads back to work
The problem seems to be solved by using the originel recPad instead of the constructor RecPad
Now I only need to get my boundries working, because the MathHelper.Clamp doesn't seem to work
See my code for more info
This code now fixed my border-problem
private void CheckBorders() {
if (recPad.Top < borders.Top)
recPad.Location = new Point(recPad.X, borders.Top);
if (recPad.Bottom > borders.Bottom)
recPad.Location = new Point(recPad.X, borders.Bottom - recPad.Height);
}
This immediately stands out to me (from CheckBounce):
movement.X *= 1;
Could be a copying error, or you forgot to put a '-'.
Also, consider using the Rectangle.Contains/Intersects method(s) to streamline some collision code, and MathHelper.Clamp to keep your paddles in bounds. This is more just for future reference, since your methods work, but it's nice to take advantage of the helpful tools in XNA.
Edit: Concerning those 'helpful tools':
The Rectangle class has the methods Intersect and Contains, which can tell you if that rectangle is intersecting another rectangle or containing a certain point, respectively. You say your ball is just a top left position and a texture, but I see in your collision checking you also check for the radius of the ball. I think you'd have an easier time defining a Rectangle bounding area for your ball and using the Intersects method to check for collision. That simplifies your collision code to:
public void CheckBounce()
{
if (myBounds.Intersects(LeftPaddle.Bounds) || myBounds.Intersects(RightPaddle.Bounds))
movement.X *= -1;
}
Fairly simple, but not entirely safe-- if the ball manages to move far enough into a paddle that one frame of movement wouldn't free it from that paddle's bounds, you'd be stuck perpetually inverting the X velocity, producing a 'stuck ball' effect. So we can add just a bit more checking code to avoid that:
public void CheckBounce()
{
if ((myBounds.Intersects(LeftPaddle.Bounds) && movement.X < 0) ||
(myBounds.Intersects(RightPaddle.Bounds) && movement.X > 0))
movement.X *= -1;
}
I apologize if the inline conditionals are a little too dense. What this means is, if the ball is moving left and hits the right paddle, invert the X. Likewise, if it's moving right and hits the left paddle, invert the X. This removes the 'sticking'.
Now, as for MathHelper.Clamp, in your situation I would use it to restrict paddle movement. MathHelper.Clamp simply clamps a value between an upper and lower bound. It's the equivalent of using Math.Min, then Math.Max.
private void CheckBorders()
{
//clamps a value to a min and a max
MathHelper.Clamp(recPad.Y, borders.Top, borders.Bottom - recPad.Height);
}
This clamps the Y position of your rectangle between the top of your borders, and the bottom of your borders minus the height of the rectangle. That last bit keeps the bottom of your rectangle from clipping the bottom of the borders by taking the height into account.