Remembering a direction, moving to a point, then changing direction. XNA - c#

I'm making a pacman clone in XNA.
So far I've drawn the tile map using 2D array, added the pills using another 2D array and made a 2D array that allows movement of pacman.
In the actual game you can press right whilst moving up, and it will wait until you're able to move right and the turn.
I have a system in place that allows a turn only when the spritePosition % 32 = 16.
This means the sprite will be centred between the walls.
I need the program to remember the last key pressed or move to the right position before turning, but i cant find a way of doing it.
Here is a bit of the code that covers what I'm trying.
public void MovementCheck()
{
presentKey = Keyboard.GetState();
spritePosition = spriteVelocity + spritePosition;
pacManRec = new Rectangle((int)spritePosition.X, (int)spritePosition.Y, pacManTex.Width, pacManTex.Height);
spriteOrigin = new Vector2(pacManRec.Width / 2, pacManRec.Height / 2);
//Press Right
if (presentKey.IsKeyDown(Keys.Right) && pastKey.IsKeyUp(Keys.Right))
{
Right();
}
}
private void Right()
{
direction = "right";
//if the next block in tile map to the right is a 1, and the sprite is centred - allow a turn
if (inputMap[(int)Math.Floor(spritePosition.Y / 32), (int)Math.Floor(spritePosition.X / 32) + 1] == 1 && (spritePosition.Y % 32 == 16))
{
rotation = ((float)Math.PI / 180);
spriteVelocity.X = 0;
spriteVelocity.Y = 0;
spriteVelocity.X = movementSpeed;
}
}
Only the right key is shown, the others are similar but the directions all change and the checks to the tile map are changed accordingly. (+1 on the X here)
ive tried things like
while (spritePosition.Y % 32 != 16)
{ spritePosition = spriteVelocity + spritePosition; }
but that just makes the sprite shoot up the screen, (kinda obviously) :(
and I tried a new Method before the Right() call
bool RightCheck()
{
if ( CONDITIONS MET HERE )
return true
else
{
//dont remember if I used this line, but something similar
spritePosition = spriteVelocity + spritePosition;
RightCheck()
}
return false; //allows program to build
}
Just an causes infinite recursion.

One solution is adding a int counter = 0; which you update in your gameloop (with counter++;) every frame/time-step. Set to 0 everytime you make a valid input and save that input.
Outlined code:
public class GameClassWhereUpdateIsDone
{
private enum Directions { None, Up, Down, Left, Right };
private int counter = 0;
private Directions currentDirection; // Current movement-direction
private Directions lastInput; // Last direction from input
public void Update(...)
{
var keyboardState = Keyboard.GetState();
if(keyboardState.IsKeyPressed(Keys.Right))
{
counter = 0;
direction = Directions.Right;
}
if(currentDirection != lastInput && counter < 5) // Allow turning 5 updates ahead.
{
// Player want to turn
if(AllowedToTurn(lastInput)
{
currentDirection = lastInput;
}
}
MoveDirection(currentDirection);
counter++;
}
private bool AllowedToTurn(Directions direction)
{
if(direction == Directions.Right)
{
return RightCheck();
}
}
}
The key idea is to keep track of movement direction and last direciton that was input...
In the original Pac-Man "pre-turning" was actually used, meaning you would start moving diagonally if you turned ahead of a corner according to: http://home.comcast.net/~jpittman2/pacman/pacmandossier.html which is an interesting read.

Related

Grid-based/Tile Movement + Collisions in Unity?

Having a huge problem figuring out how to do a grid-based movement or tile based movement. Somewhat similar to Nitrome's Redungeon: Can't really post an image: so here's a gif. http://www.eneminds.com/redungeon/img/gifs/gif_1.gif
I figured I should create a grid system. I thought this was easy just like the old games(Pokemon etc.) Not sure how to do that though. But I also want the movement to be fast.
Unity has its own implementation of tilemaps which should allow you to build the background a.k.a the ground you walk on:
https://docs.unity3d.com/Manual/Tilemap.html
Furthermore, Unity provides more 2d content to help you build things right here:
https://github.com/Unity-Technologies/2d-extras
Collisions are outlined in numerous tutorials found on here:
https://unity3d.com/learn/tutorials/s/2d-game-creation
To make a game like the one in the animated GIF, is not easy. This is especially the case if you're new to game dev, so don't expect easy solutions.
You might want to check out the Unity Asset Store for a quick 'n dirty way of getting tile-based movement, but this as always will come with a price and will not help you learn.
https://assetstore.unity.com/
With regards to movement speed, this is always just a variable in your game which can be tuned to your preference. In the case of a tile based game it would correspond to the time it takes for the character to be visually shifted from one tile to another. Try reading up on Lerping which is a part of the Unity engine.
https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
I was able to pull it off.
I just modified the input a little from the RogueLike Tutorial from Unity's Learn Section.
So This is the code responsible for movement
void Move(string direction_string)
{
ChangeDirection(direction_string);
Vector2 start = transform.position;
Vector2 end = start + direction;
boxCollider.enabled = false;
hit = Physics2D.Linecast(start, end, blockingLayer);
boxCollider.enabled = true;
if (hit.transform == null) {
StartCoroutine(Movement(end));
animator.SetTrigger(direction_string);
return;
} else {
moveSequence.RemoveAt(0);
animator.SetTrigger(direction_string);
return;
}
}
void ChangeDirection(string direction_string)
{
switch (direction_string) {
case "up":
direction = dir_Up;
break;
case "down":
direction = dir_Down;
break;
case "left":
direction = dir_Left;
break;
case "right":
direction = dir_Right;
break;
}
}
IEnumerator Movement(Vector3 end)
{
moving = true;
float sqrRemainingDistance = (transform.position - end).sqrMagnitude;
while (sqrRemainingDistance > float.Epsilon) {
Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, moveSpeed * Time.deltaTime);
rb2D.MovePosition(newPosition);
sqrRemainingDistance = (transform.position - end).sqrMagnitude;
yield return null;
}
currentPos = end;
moveSequence.RemoveAt(0);
moving = false;
}
}
Here is what's responsible for input
void FixedUpdate ()
{
if (moveSequence.Count > 0 && !moving) {
Move(moveSequence[0]);
}
}
Then just hook it up to an Update() function that listens to button press and add a list item to moveSequence List like
moveSequence.Add("up");
Just did a code like this for someone on reddit, came here to see if its been asked before. I know I'm 2 years late but here it goes in case anyone needs it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameController : MonoBehaviour
{
// a script created so that a player always moves one direction at a time on a grid.
// It uses a plane with even width and height to make a grid
// - if needed, turn off the mesh renderer so that you dont see the tiled grid.
// the game object we use to create a grid. Should be a plane with an even width and height.
public GameObject tile_object;
// grid x and y sizes, set them to odd numbers
public float x_grid_size, y_grid_size;
// List of all our floor tiles
Dictionary<(float x, float y), GameObject> floor_list = new Dictionary<(float x, float y), GameObject>();
// Reference to the player prefab
public GameObject player;
// reference to player character
private GameObject player_character;
// a tool used for debugging
bool debugging = true;
//check if player is moving
bool player_is_moving = false;
(float x, float y) players_current_tile;
// players speed
public float player_speed = 35f;
// used in calculating movements
(float x, float y) to_tile;
//used to calculate movement
Vector3 to_pos;
// Start is called before the first frame update
void Start()
{
// put the player into the scene
player_character = Instantiate(player, Vector3.zero, player.transform.rotation);
// Sets the Middle Tile and checks grid is odd
SetMiddleTile();
// create a floor grid to help us visualize our play field.
CreateGrid();
}
// Update is called once per frame
void Update()
{
PlayerMovement();
MovePlayer();
}
// gets inputs on A S W D keys for movement
void PlayerMovement() {
if (Input.GetKeyDown(KeyCode.W)){
PlayerInput((0, 1));
}
if (Input.GetKeyDown(KeyCode.A))
{
PlayerInput((-1, 0));
}
if (Input.GetKeyDown(KeyCode.S))
{
PlayerInput((0, -1));
}
if (Input.GetKeyDown(KeyCode.D))
{
PlayerInput((1, 0));
}
}
// simplifies player entries, and gets input directional data
void PlayerInput((float x, float y) input)
{
if (!player_is_moving)
{
(float x, float y) to_tile_check = (players_current_tile.x + input.x, players_current_tile.y + input.y);
if (floor_list[to_tile_check] != null)
{
player_is_moving = true;
to_tile = (players_current_tile.x + input.x, players_current_tile.y + input.y);
to_pos = floor_list[to_tile].transform.position;
}
}
}
// used to move the player
void MovePlayer()
{
if (player_is_moving)
{
player_character.transform.position = Vector3.MoveTowards(player_character.transform.position, to_pos, Time.deltaTime * player_speed);
if (player_character.transform.position == to_pos)
{
players_current_tile = to_tile;
player_is_moving = false;
}
}
}
// used to create the floor grid
void CreateGrid()
{
// Create an empty to hold our floor grid
GameObject grid_empty = new GameObject();
grid_empty.name = "grid_floor";
// get the size of our floor tile so we know how far to space them.
float tile_size = tile_object.GetComponent<MeshRenderer>().bounds.size.x;
// create an offset so the grid is always centered in game view
Vector3 offSet = new Vector3((x_grid_size * tile_size) / 2 - (tile_size / 2), 0, (y_grid_size * tile_size)/2 - (tile_size / 2));
// iterate to create tiles on x axis
for (int x = 0; x < x_grid_size; x++)
{
// iterate to create tiles on y axis
for (int y = 0; y < y_grid_size; y++)
{
// instantiate new tile
GameObject floor_tile = Instantiate(tile_object, (new Vector3(x * tile_size, 0, y * tile_size) - offSet), tile_object.transform.rotation);
floor_tile.name = "flr_x: " + (x + 1) + " y:" + (y + 1);
// set the parent to grid empty so the scene isnt filled with objects and all floor tiles are neatly centerd
floor_tile.transform.parent = grid_empty.transform;
// add the tile to our dictionary.
floor_list.Add(((x + 1),(y + 1)), floor_tile);
}
}
if (debugging) { print("Created Floors: " + (x_grid_size * y_grid_size) + " Floor List Size: " + floor_list.Count);}
}
// sets the middle tile the player is spawned on, also checks to make sure grid size is set correctly
void SetMiddleTile()
{
// these check to make sure grid size isnt 0 and are odd
if (x_grid_size == 0 && y_grid_size == 0) { print("Forgot to set grid size! setting default to 7x and 7y"); x_grid_size = 7; y_grid_size = 7; }
if (x_grid_size % 2 == 0) { print("x_grid_size is set to an even number(" + x_grid_size + "), changing it to odd(" + (x_grid_size + 1) + ")"); x_grid_size += 1; }
if (y_grid_size % 2 == 0) { print("y_grid_size is set to an even number(" + y_grid_size + "), changing it to odd(" + (y_grid_size + 1) + ")"); y_grid_size += 1; }
// splits the grid and half
float x = x_grid_size / 2 + 0.5f;
float y = y_grid_size / 2 + 0.5f;
// set the players current tile to middle tile
players_current_tile = (x, y);
// set the to tile to current tile to avoid null instances when first checking our dictionary
to_tile = players_current_tile;
// used for debugging
if (debugging) { print("the middle tile is: x(" + x + ")" + " y(" + y + ")"); }
}
}

How do I get a component from a ever changing List of objects?

while working in a 3d endless runner game in unity I came across this issue. I have a List of platforms(segments/roads) that lay in front of the player while the player runs in z direction. I have downloaded a new asset package called Dreamteck splines. So each platform has a spline component attached to it. Once a platform is laid the player grabs the spline and runs according to the pattern of the spline.
Let's say that the player is on the first platform. When the player reaches the end of the first platform's spline, the OnEndReached() event handler is called, which basically says what you want to happen when the spline's endpoint is reached. So I want to know how to I get the next spline once the end is reached.
P = player
As seen in the image above this is what I am trying to accomplish. As a brief description of how platforms are laid is that once the player goes to the next road the one he just passed gets disabled so next time he can reuse the road in front of the player in random manner.
The code: track manager script.
public Segment[] tilePrefabs;
public static Segment newSegment;
public static List<Segment> m_Segments;
public static List<Segment> m_PastSegements;
private int m_SafeSegmentLeft;
private int m_PreSegments = -1;
private float startingSegmentDistance = 4f;
private int startingSafeSegments = 2;
private int amtSegmentsOnScreen = 10;
private float segmentRemovalDistace = -40f;
private float m_TotalWorldDistance;
private float m_CurrentSegmentDistance;
void Update ()
{
while (m_Segments.Count < amtSegmentsOnScreen)
{
SpawnNewSegment();
}
m_TotalWorldDistance += scaledSpeed;
m_CurrentSegmentDistance += scaledSpeed;
if (m_CurrentSegmentDistance > m_Segments[0].worldLength)
{
m_CurrentSegmentDistance -= m_Segments[0].worldLength;
m_PastSegements.Add(m_Segments[0]);
m_Segments.RemoveAt(0);
}
Vector3 currentPos;
Quaternion currentRot;
Transform playerTransform = playerMotor.transform;
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
bool needRecenter = currentPos.sqrMagnitude > floatingOriginThreshold;
if (needRecenter)
{
int count = m_Segments.Count;
for (int i = 0; i < count; i++)
{
m_Segments[i].transform.position -= currentPos;
}
count = m_PastSegements.Count;
for (int i = 0; i < count; i++)
{
m_PastSegements[i].transform.position -= currentPos;
}
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
}
playerTransform.rotation = currentRot;
playerTransform.position = currentPos;
for (int i = 0; i < m_PastSegements.Count; i++)
{
if ((m_PastSegements[i].transform.position - currentPos).z < segmentRemovalDistace)
{
m_PastSegements[i].Cleanup();
m_PastSegements.RemoveAt(i);
i--;
}
}
}
public void SpawnNewSegment()
{
int useSegment = Random.Range(0, tilePrefabs.Length);
if (useSegment == m_PreSegments)
{
useSegment = (useSegment + 1) % tilePrefabs.Length;
}
Segment segmentToUse = tilePrefabs[useSegment];
newSegment = Instantiate(segmentToUse, Vector3.zero, Quaternion.identity);
Vector3 currentExitPoint;
Quaternion currentExitRotation;
if (m_Segments.Count > 0)
m_Segments[m_Segments.Count - 1].GetPointAt(1.0f, out currentExitPoint, out currentExitRotation);
else
{
currentExitPoint = transform.position;
currentExitRotation = transform.rotation;
}
newSegment.transform.rotation = currentExitRotation;
Vector3 entryPoint;
Quaternion entryRotation;
newSegment.GetPointAt(0.0f, out entryPoint, out entryRotation);
Vector3 pos = currentExitPoint + (newSegment.transform.position - entryPoint);
newSegment.transform.position = pos;
newSegment.manager = this;
newSegment.transform.localScale = new Vector3((Random.value > 0.5f ? -1 : 1), 1, 1);
newSegment.objectRoot.localScale = new Vector3(1.0f / newSegment.transform.localScale.x, 1, 1);
if (m_SafeSegmentLeft <= 0)
SpawnObstacle(newSegment);
else
m_SafeSegmentLeft -= 1;
m_Segments.Add(newSegment);
}
The player script
//Current tile segment;
private Segment currentSegment;
//Spline Follower
private SplineFollower follower
//For Dreamteck spline -->
private Segment nextSegment;
void Start()
{
playerCollider = GetComponent<CapsuleCollider>();
anim = GetComponent<Animator>();
follower = GetComponent<SplineFollower>();
moveLane = currentLane;
follower.onEndReached += Follower_onEndReached;
}
private void Follower_onEndReached()
{
currentSegment = nextSegment;
follower.computer = currentSegment.spline;
}
void OnTriggerEnter(Collider col)
{
nextSegment = col.GetComponentInParent<Segment>();
}
The segment script : Attached to each road/ platform
public SplineComputer spline;
public static Segment next;
SplinePoint[] points;
void Start()
{
spline = GetComponentInChildren<SplineComputer>();
spline.space = SplineComputer.Space.Local;
points = spline.GetPoints();
if (points.Length == 0)
return;
}
At the moment I use colliders, each road has a box collider component. Once the player reach end of the platform it does get the next spline component. It works but sometimes it fails to recognize the next spline and use the same spline which causes the player to run the same platform that he passed again and again.
So I'm out of ideas. So came here to find a solution or advice. Help would be appreciated.
In this case I would simply store my possible segments in a List then when I reached the end get the next segment and move the current 1st segment to the end or where ever you want to move it in the list.
That is
public Segment currentSegment;
public List<Segment> segments;
void OnEndReached()
{
//Put the completed segmetn back in the list ... probably at the end or randomized anywhere but at the start
segments.Insert(segments.Count-1, currentSegment);
//Now get the next segment
currentSegment = segments[0];
segments.RemoveAt(0);
}
In this model you have a simple List which represents the order your segments will appear in, you always set the current segment to the next in the list e.g. index 0 and you put them back in the list when done... putting them at the end or if you want to randomize order slotting them in anyware except index 0 e.g.
segments.Insert(UnityEngine.Random.Range(1, segments.Count), currentSegment);
Note that I removed the segment I am on from the list ... the list just represents the upcoming order which in runner games I find it handy to know that e.g. so I can reset things, change attributes of the segments based on performance, score, etc.
Using OnTriggerEnter, or OnTriggerEnter2D if you're dealing with 2D colliders, should do the job. But as you say you already are working with colliders, I assume this is what you have tried.
You could try:
OnTriggerStay
A Raycast down to the ground object. In this link is a 2D Example: https://kylewbanks.com/blog/unity-2d-checking-if-a-character-or-object-is-on-the-ground-using-raycasts
You can also raycast with 3D objects.
What you would be using it for in this case is basically to shoot a "laser" into the ground below your player and grab an object there based on which layers you tell it to hit. So if you have a layer called "Ground" which your platform is part of, then it can only return objects from that layer.
Just remember to raycast often so it's updated to reflect the game.

How to Move disabled gameObjects from one list to another?

I'm been making an endless runner game. I have a set of tiles (4 atm) which I spawn randomly. For optimization purposes I decided instead of instantiating and destroying game objects once the player run though it do disable them and once a when a tile is needed to spawn from the deactivated tiles. I made two lists active and deactivate which will do the above scenario. I managed to disable them but I don't know how to add them to the disable list and spawn them when. needed. How can I achieve it.
Here's the code and what I did so far.
TileManager.cs
void Update ()
{
//Vector3 offset = new Vector3(0f, 0f, Time.time * speed);
//GameObject go;
//go.transform.SetParent(transform);
if (playerTransform.position.z - safeZone > (spawnZ - amtOfTilesOnScreen * tileLength))
{
SpawnTile();
DeleteTile();
}
}
private void DeleteTile()
{
//Destroy(activeTiles[0]);
activeTiles[0].SetActive(false);
activeTiles.RemoveAt(0);
//deactivatedTiles.RemoveAt(0);
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
if (prefabIndex == -1)
{
go = Instantiate(tilePrefabs[RandomPrefabIndex()]) as GameObject;
}
else
{
go = Instantiate(tilePrefabs[prefabIndex]) as GameObject;
}
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
activeTiles.Add(go);
//go.SetActive(false);
//if (deactivatedTiles != null)
//{
// Debug.Log("Has a Tile");
// deactivatedTiles[0].SetActive(true);
//}
//Debug.Log(deactivatedTiles);
}
A help would be greatly appreciated.
Instead of deactivating and then re-activating your tiles, you could just move them from the back to the front when needed, like this:
|---|---|---|---|
|---|---|---|---|
|
V
|---|---|---|---|
|---|---|---|---|
(The first picture is the old state, and the second one is the state after). Instead of
if (playerTransform.position.z - safeZone > (spawnZ - amtOfTilesOnScreen * tileLength)) {
SpawnTile();
DeleteTile();
}
Do something like
if (playerTransform.position.z - safeZone > (spawnZ - amtOfTilesOnScreen * tileLength)) {
if (activeTiles.Count() < (NumberOfTilesThatFitOnScreen + 1)) {
var spawnedTile = SpawnTileAtFront();
activeTiles.Add(spawnedTile);
} else {
var movedTile = activeTiles[0];
MoveTileToFront(movedTile);
//Puts the moved tile at the end of the array, so the next tile that will be moved is always the one in back
activeTiles.RemoveAt(0);
activeTiles.Add(movedTile);
}
}
and then implement these two methods like so:
private void MoveTileToFront(GameObject tile)
{
tile.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
}
private GameObject SpawnTileAtFront(int prefabIndex = -1)
{
GameObject go;
if (prefabIndex == -1)
{
go = Instantiate(tilePrefabs[RandomPrefabIndex()]) as GameObject;
}
else
{
go = Instantiate(tilePrefabs[prefabIndex]) as GameObject;
}
go.transform.SetParent(transform);
MoveTileToFront(go);
return go;
}
Set NumberOfTilesThatFitOnScreen to the number of tiles that completely fill the screen.
Now your game should keep spawning new tiles until there is enough so that if you alter the last or first tile, it won't show on the screen. Then, once this amount is reached, the game should take the last tile (which isn't on screen) and move it to the front (which also shouldn't be on the screen). Then you're character will encounter this tile again, and another off-screen tile will be moved to the front, and so on. Ideally, it would look like the character is on an infinite path
Just move the tile from the active list to the deactivate list, like this:
private void DisposeActiveTile(int index)
{
GameObject unused_tile = activeTiles[index];
activeTiles.RemoveAt(index);
deactivatedTiles.Add(unused_tile);
}
private GameObject GetLastUnusedTile()
{
if(deactivatedTiles.Count == 0)
return null;
int last_index = deactivatedTiles.Count - 1;
GameObject unused_tile = deactivatedTiles[last_index];
deactivatedTile.RemoveAt(last_index);
return unused_tile;
}
When removing an object from the list, the object is not destroyed if it is referenced by another variable.

having trouble implementing physics to ball collision program

I'm currently working on a simple ball collision program in C#, where an indeterminate amount of balls bounce around the screen, each being added to the program on a button click. So far I've gotten everything working, and I have a basic collision system. Now, I'm trying to add more realistic physics to the balls.
The thing is, I have little understanding of physics. I've been reading a bit about vector physics and I get the gist of it, but I'm not quite making the connection between what I've read and how I'm supposed to implement these concepts programmatically. Could anybody provide me some resources on how one is actually supposed to implement vector physics to something like a simple ball collision program like I've written?
Here's what I have right now. It's basically just two booleans determining whether my ball goes in one of four directions. When two balls collide, they just pass their directions to each other.
public void newball(PaintEventArgs e, int panelWidth, int panelHeight)
{
//Detects walls of form. if it detects a wall or another ball, it bounces
if (X >= panelWidth)
{
bounceX = true;
}
else if (X <= 25)
{
bounceX = false;
}
if (Y >= panelHeight )
{
bounceY = true;
}
else if (Y <= 25)
{
bounceY = false;
}
//balls only go in four directions right now have to implement vector physics.*/
if (bounceX == false)
{
X = X+2;
}
else if (bounceX == true)
{
X = X -2;
}
if (bounceY == false)
{
Y = Y+2;
}
else if (bounceY == true)
{
Y = Y-2;
}
//this draws the ball on the panel.
Pen clsPen = Pens.Black;
Color color = Color.Black;
SolidBrush brush = new SolidBrush(color);
e.Graphics.DrawEllipse(clsPen, X - 25, Y -25, 25, 25);
e.Graphics.FillEllipse(brush, X-25, Y-25, 25, 25);
}
And here are some of the things I've read:
2D Collisions, Vectors

Player does not stop when colliding with block [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm trying to make it so my player comes to a complete halt when he hits a block, but can become unstuck if he presses a certain key(s). Example: Player is going down and hits a block. He stops, but if presses W, A, or D, he can move up, left, or right. Here's what I currently have for the code.
In this case, sp means speed, blocksp means his speed when he is being blocked. (This.Sprite), in this case, is referring to the block that stops him.
bool Blocked = false;
float Bottom = -32;
float Left = -32;
float Right = This.Sprite.GetWidth() - 32;
float Top = This.Sprite.GetHeight() - 32;
int x = 0;
int y = 0;
float sp = 1.59f;
float blocksp = 0.00f;
Sprite Player = This.Game.FindSprite("GuySprite");
if (This.Sprite.CollisionWithSprite("GuySprite") != null)
{
if (Player.Position.Y > Top)
{
Blocked = true;
Player.Velocity = new Point2D(x, y) * blocksp;
Player.Animation = 0;
if (This.Game.IsPressed(InputKey.W) || This.Game.IsPressed(InputKey.A) || This.Game.IsPressed(InputKey.D))
{
Blocked = false;
Player.Velocity = new Point2D(x, y) * sp;
Player.Position.Y -= 0.85f;
}
}
if (Player.Position.Y < Bottom)
{
Blocked = true;
Player.Velocity = new Point2D(x, y) * blocksp;
Player.Animation = 0;
if (This.Game.IsPressed(InputKey.S) || This.Game.IsPressed(InputKey.A) || This.Game.IsPressed(InputKey.D))
{
Blocked = false;
Player.Velocity = new Point2D(x, y) * sp;
Player.Position.Y += 0.85f;
}
}
if (Player.Position.Y < Right)
{
Blocked = true;
Player.Velocity = new Point2D(x, y) * blocksp;
Player.Animation = 0;
if (This.Game.IsPressed(InputKey.S) || This.Game.IsPressed(InputKey.A) || This.Game.IsPressed(InputKey.W))
{
Blocked = false;
Player.Velocity = new Point2D(x, y) * sp;
Player.Position.Y -= 0.85f;
}
}
if (Player.Position.Y > Left)
{
Blocked = true;
Player.Velocity = new Point2D(x, y) * blocksp;
Player.Animation = 0;
if (This.Game.IsPressed(InputKey.S) || This.Game.IsPressed(InputKey.W) || This.Game.IsPressed(InputKey.D))
{
Blocked = false;
Player.Velocity = new Point2D(x, y) * sp;
Player.Position.X += 0.85f;
}
}
}
Currently, if my player touches any side of the block and continues his movement that the block stops, the player will move in an erratic direction and does not stop at the block. Any ideas?
Edit
Ok
The player is running towards the block so he'll hit the left side in the next frames.
So that the following condition ll be true.
if (This.Sprite.CollisionWithSprite("GuySprite") != null)
Now you have to implement a method to find out which side the player collided with.
Actually your code is confusing me since:
Player.Velocity = new Point2D(x, y) * ...;
will always be the zero vector because x = y = 0
I'd do it like this :
float xDistance=0f, yDistance=0f;//this ll be explained later on
If(Player.Velocity.X > 0) //he is running to the right so he can hit the left side
xDistance=mayHitLeft();
Else If(Player.Velocity.X < 0)
xDistance=mayHitRight();
If(Player.Velocity.Y > 0) //In my case positive Y means downwards so can hit top
yDistance=mayHitTop();
Else If(Player.Velocity.Y < 0)
yDistance=mayHitBot();
Now we have to find out if the player hit the bot/top edge or the left/right edge or maybe both.
let's assume he is falling and was running to the right before.
so Player.Velocity.X > 0 and Player.Velocity.Y > 0
This means the function mayHitLeft(); and mayHitTop();
will be called.
I don't know how the collisionWithSprite function works.
But as always we take the worst case like this one blue is the block and orange the player.
This is the frame before hitting and no collision was detected by now. so in the next frame both functions would detect a collision but which is the right one. As we see it is colliding with the top. So how to detec the right one? Therefore we use the distance or let's say how much overlapping we have in which direction. So less overlap is the right one.
So both functions return a double. Let's look at the mayHitTop() function
private float mayhitTop()
{
//remember that the y coordinate goes downwards so we have to add
float playersBotCoordinate = Player.Position.Y + Player.Sprite.Height;
//the y position is the top so nothing to change
float blocksTopCoordinate = Block.Position.Y;
hitTop=true; //this is a global variable you have 1 for each direction
return Math.Abs(playersBotCoordinate - blocksTopCoordinate);
}
So we have set the xDistance and yDistance and we know that it will hit the top or left.
Now we have to compare everything
// this means there could be 2 sides like our case that can be ths possible collision edge
if((hitLeft || hitRight) && (hitTop || hitBot))
{
//we hit the left or right side
if(xDistance<yDisante)
{
Player.Velocity.X = 0;
}
//we hit top or bottom
else
{
Player.Velocity.Y=0;
}
}
else
{
if(hitLeft || hitRight)...
}
Maybe you have to unstuck the player. Otherwise I think there could be some problems.
So I just wrote it without any template. Don't know if there are any Syntax mistakes. but this shall only give you an overview how to do it ´we can expect this to be pseudocode ;)
Hope it'll help you at least a little.
Old
Ehhhh you are comparing the player position.Y with right and left
this should be the X coordinate if I understood the code ;)
if (Player.Position.Y < Right)
and
if (Player.Position.Y > Left)

Categories