XNA 2D object collision (without Tiles/Grid) - vb.net

First time posting here. Tried to look for topics previously to help.
I'm using Visual Basic, but so far I've been able to follow C# and just translate into VB.
I would like collision without tiles. Smooth movement without any sort of snapping. I already have the movement down, and my sprites stop at the edges of the screen.
I've read I could use Bounds and Intersects, which I have tried. When I apply an IF statement to the arrow keys each time they are pressed, using Bounds and Intersects (I just prevent sprite movement if it is intersecting), it works for ONE key. I move left into an object, and I stop. If I apply the IF to all keys, it will work the first time. Say I move left into an object, the IF statement checks if the Intersects is true or not and acts accordingly.
Now I want to move right, away from the object. I can't since my sprite is ALREADY colliding with the object, since each arrow key is programmed to NOT move if there is Intersection. I see perfectly why this happens.
The code I currently have: (Each arrow key has the same code, altered to it)
If Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right) And rBlockBounds.X <=
graphics.GraphicsDevice.Viewport.Width - rBlockBounds.Width = True Then
If rBlockBoundBoxBounds.Intersects(rObstructBounds) Then
rBlockBounds.X += 0
rBlockBoundBoxBounds.X = rBlockBounds.X - 1
Else
rBlockBounds.X += 1
rBlockBoundBoxBounds.X = rBlockBounds.X - 1
End If
End If
rBlockBounds is my sprite As Rectangle
rBlockBoundBoxBounds is another Rectangle (1 pixle bigger than rBlockBounds) used as a Hit Box more or less that moves with rBlockBounds, and is the thing doing the collision checking
rObstructBounds is the stationary object that I'm moving my Sprite into.
Anyone have suggestions on how I can make this work?

Since I myself program in C#, not VB I can not code your solution but instead I can explain a better way of approaching it.
What you want to do is prevent the two rectangles from ever intersecting. To do this you will need to implement a move method into your code which can check if the two tiles are colliding. Here is a C# example:
public bool MoveX(float distance) // Move Player Horizontally in this example
{
rBlockBounds.X += distance;
if(rBlockBoundBoxBounds.Intersects(rObstructBounds)
{
rBlockBounds.X -= distance;
return false;
}
return true;
}
Which essentially means that if you run into an object you will be pushed out of it. Since it occurs in one tick you won't get any jutty back-and-front animations.
And that should do what you want. You can test this out and then implement it for y-coordinates as well.
Also, you might notice I've made the function return a bool. This is optional but allows you to check if your player has moved or not.
Note if you teleport an object into another one it will cause problems so remember to implement this every time you move anything.
But that should do what you want.
Edit
Note since your objects are not in a tiled grid, you will need to move lots of time in very small steps.
Something like this:
public bool MoveX(float distance) // Move Player Horizontally in this example
{
rBlockBounds.X += distance;
if(rBlockBoundBoxBounds.Intersects(rObstructBounds)
{
rBlockBounds.X -= distance;
return false;
}
return true;
}
public bool MoveX(float distance, int repeat)
{
for(int i=0; i < repeat; i++)
{
rBlockBounds.X += distance;
if(rBlockBoundBoxBounds.Intersects(rObstructBounds)
{
rBlockBounds.X -= distance;
return false;
}
}
return true;
}
Where the second one will take multiple steps. Here is why you would use it:
MoveX(500); // Will move 500 right. Could easily skip over objects!
MoveX(5, 100); // Will move 5 right one hundred times
// ^ This will take more time but will not skip object
Similarly for yours you could do this:
MoveX(3); // If contact object will be max 3 pixels away
MoveX(1, 3); // If contact object will be max 1 pixels away
MoveX(0.5f, 6); // If contact object will be max 0.5 pixels away
Now I am guessing all your x, y positions are integers. If so you could get away doing the second call and come exactly next to each other. If not you would do the third call.
Hope this helped.

Related

I'm looking for a solution for a loop with a array of images

so what i'm trying to do is to change the images that's stored in a array that contains 16 imgs, it's basically a animation for when you play a game (press forward character moves[moving imgs], release button character stops[stoped img]). The thing is that after the array reaches it's length max and the key is still being pressed it needs to reset the array to 15 (so this loop will give the illusion of the constant movement), but i think i'm not finding the right synthax or something.
Thanks in advance.
PImage p1;
PImage[] zeroArray = new PImage [16];
void setup() {
size(600,600);
for(int i = 0; i < zeroArray.length; i++){
zeroArray[i] = loadImage (i + ".png");
}
}
void draw() {
background(255);
imageMode(CENTER);
if(keyPressed) {
i++;
} else if (i > zeroArray.length) {
zeroArray[] = zeroArray[15]; // <--this ain't right, how should i declare this??
}
image(zeroArray[i], 300, 300, 800, 800);
}
I think you're mixing a few ideas into one if statement. Let's take a look at this section of your code:
if(keyPressed){
i++;
}
else if (i > zeroArray.length){
zeroArray[] = zeroArray[15];
}
Run through this line-by-line and really think about what it's doing. If a key is pressed, it increments the i variable. If a key is not pressed, then it checks whether i is greater than the length of the zeroArray array variable. If it is, it attempts to reset the array, which is not valid syntax.
Take a step back and think about what you're trying to do. Forget about code for a second. Write down exactly what you're trying to do, in English. My guess is you're trying to do something like this:
Change the image being drawn whenever the key is pressed.
If we reach the end of the images, then start over at the first image.
When you have something like that written out, that's an algorithm that you can start thinking about implementing with code. Break your problem down into smaller steps and take those steps on one at a time.
For example, step 1 is to change the image whenver the key is pressed. You basically have that already:
if(keyPressed){
i++;
}
Then, step 1 is to reset the i variable whenever it reaches the end of the array. You might do that using an if statement like this:
if(i >= zeroArray.length){
i = 0;
}
Putting it all together, it would look like this:
if(keyPressed){
i++;
if(i >= zeroArray.length){
i = 0;
}
}
Now this code checks if a key is being pressed, and if so it increments the i variable. It then checks whether the i variable has gone past the end of the array, and if so it reset it back to the beginning.
If that's not exactly what you're looking for, then the process of solving your problem is still the same: you need to write down, in English, exactly what you're trying to do. You need to do that before you start hammering away at the code.
Are you sure you want to reset the array to 15? That's the last element. However, in general, the solution would be to change the line:
zeroArray[] = zeroArray[15];} // <--this ain't right, how should i declare this??
to
i = 15; //Just change the index pointer to 15 (or whichever frame you want to reset to)
Thank you all for the answers, they really gave me some light.
To Kevin Workman.
Writing down the path that i intended to take really helped out, your thinking was almost on point. Using your method i came up with this
1)Change character sprites if key is being pressed
2) Reset movement to a certain point in case it exceeded the animation sprites, and the key is still being pressed
3)Set to fist sprite if the key is released or not being pressed at all
To Sahil Jain
You were right about changing the index to [15], it didn't work so well
Here is the revised code:
PImage p1;
PImage[] zeroArray = new PImage [16];
void setup(){
size(600,600);
for(int i = 0; i < zeroArray.length; i++){
zeroArray[i] = loadImage (i + ".png");
}
}
void draw(){
background(255);
imageMode(CENTER);
if (keyCode == RIGHT){
if(keyPressed){
i++;
} else {
i = 0;
}
if (i >= zeroArray.length){
i = 4;}
image(zeroArray[i], 300, 300, 800, 800);
}
}
Have you tried switching your else if to your main argument in the last If...Then statement? Also, don't forget to reset your variable i to 0 so it resets too and doesn't continue to always try to reset and end up in an infinite loop of sorts :)
As it is in your code in the question, it does the increment first and will never follow through to read into the second choice (the one you want it to do).

How to make large 2d tilemap easier to load in Unity

I am creating a small game in the Unity game engine, and the map for the game is generated from a 2d tilemap. The tilemap contains so many tiles, though, is is very hard for a device like a phone to render them all, so the frame rate drops. The map is completely static in that the only moving thing in the game is a main character sprite and the camera following it. The map itself has no moving objects, it is very simple, there must be a way to render only the needed sections of it or perhaps just render the map in once. All I have discovered from researching the topic is that perhaps a good way to do it is buy using the Unity mesh class to turn the tilemap into a mesh. I could not figure out how to do this with a 2d tilemap, and I could not see how it would benefit the render time anyways, but if anyone could point me in the right direction for rendering large 2d tilemaps that would be fantastic. Thanks.
Tile system:
To make the tile map work I put every individual tile as a prefab in my prefab folder, with the attributes changed for 2d box colliders and scaled size. I attribute each individual prefab of the tile to a certain color on the RGB scale, and then import a png file that has the corresponding colors of the prefabs where I want them like this:
I then wrote a script which will place each prefab where its associated color is. It would look like this for one tile:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Map : MonoBehaviour {
private int levelWidth;
private int levelHeight;
public Transform block13;
private Color[] tileColors;
public Color block13Color;
public Texture2D levelTexture;
public PlayerMobility playerMobility;
// Use this for initialization
void Start () {
levelWidth = levelTexture.width;
levelHeight = levelTexture.height;
loadLevel ();
}
// Update is called once per frame
void Update () {
}
void loadLevel(){
tileColors = new Color[levelWidth * levelHeight];
tileColors = levelTexture.GetPixels ();
for (int y = 0; y < levelHeight; y++) {
for (int x = 0; x < levelWidth; x++) {
// if (tileColors [x + y * levelWidth] == block13Color) {
// Instantiate(block13, new Vector3(x, y), Quaternion.identity);
// }
//
}
}
}
}
This results in a map that looks like this when used with all the code (I took out all the code for the other prefabs to save space)
You can instantiate tiles that are in range of the camera and destroy tiles that are not. There are several ways to do this. But first make sure that what's consuming your resources is in fact the large number of tiles, not something else.
One way is to create an empty parent gameObject to every tile (right click in "Hierarchy" > Create Empty"
then attach a script to this parent. This script has a reference to the camera (tell me if you need help with that) and calculates the distance between it and the camera and instantiates the tile if the distance is less than a value, otherwise destroys the instance (if it's there).
It has to do this in the Update function to check for the distances every frame, or you can use "Coroutines" to do less checks (more efficient).
Another way is to attach a script to the camera that has an array with instances of all tiles and checks on their distances from the camera the same way. You can do this if you only have exactly one large tilemap because it would be hard to re-use this script if you have more than a large tilemap.
Also you can calculate the distance between the tile and the character sprite instead of the camera. Pick whichever is more convenient.
After doing the above and you still get frame-drops you can zoom-in the camera to include less tiles in its range but you'd have to recalculate the distances then.

How to determine if an object is clicked?

Suppose I have an array of objects Ball that are floating around in the canvas, and if an object is clicked, it will disappear. I am having a hard time thinking how to know if an object is clicked. Should I use for loop to loop through if the mouse position is within the area of those objects? But I am afraid that will slow down the progress. What is a plausible algorithm to achieve this?
Keep track of the various centre points and radius of the Balls, and whenever a mouse click happens, calculate the distance of the mouse co-ordinates to the other balls centres. If any distance comes out to be within the radius of the particular ball, that means that, that particular ball was clicked.
public class Ball {
private Point centre;
private int radius;
public boolean isInVicinityOf(int x, int y)
{
// There are faster ways to write the following condition,
// but it drives the point I'm making.
if(Math.hypot(centre.getX() - x, centre.getY() - y) < radius)
return true;
return false;
}
// ... other stuff
}
Here's a code for checking if mouse click happened on any ball:
// Returns the very first ball object which was clicked.
// And returns null if none was clicked.
public Ball getBallClicked(Ball[] balls, MouseEvent event)
{
for (Ball ball : balls)
{
if(ball.isInVicinityOf(event.getX(), event.getY()))
{
return ball;
}
}
return null;
}
There are many other ways to go about implementing the same thing, like by using Observer pattern and others, but above is one of those approach.
Hope it helps.
Use void mouseClicked() it then specify its cordinates on the screen. You can specify what you want to do with the object in an if-statement in that void.

How to make a number of balls bounce?

I need to change the "bounce" method in the BallDemo class to allow a user to choose how many bouncing balls there are through a parameter. I need to use a collection to store the balls, and the balls should be placed in a row along the top of the default canvas. Also, the balls should erase when the last ball reaches the end of the canvas. I know I need to use an array list and a for loop for the draw, move, and erase methods but other than that I'm stuck. This is what I have so far:
/**
* Simulates a chosen number of specific balls bouncing.
*/
public void bounce(int amount)
{
int ground = 400; // position of the ground line
myCanvas.setVisible(true);
// draw the ground
myCanvas.drawLine(50, ground, 550, ground);
***ArrayList<BouncingBall> balls = new ArrayList<BouncingBall>();
for(int index; index < amount; index++)
{
BouncingBall ball = new BouncingBall(0, 0,***
// make them bounce
boolean finished = false;
while(!finished)
{
myCanvas.wait(50); // small delay
ball.move();
ball2.move();
// stop once ball has travelled a certain distance on x axis
if(ball.getXPosition() >= 550 && ball2.getXPosition() >= 550)
finished = true;
}
ball.erase();
ball2.erase();
}
}
The program right now is set to make two balls bounce because it didn't have a parameter at first, it was programmed to make two bouncing balls. The lines with the ArrayList and for loop are what I added but I'm stuck on there. Also, the default canvas has a width of 600 and a height of 500.

Problems in my AS2 Game

Hey guys, I'm trying to make a 2D Platform style game similar to this game below:
http://www.gameshed.com/Puzzle-Games/Blockdude/play.html
I have finished making most of the graphic, and areas, and collision, but our character is still not able to carry things. I'm confused as to what code to use so that my character can carry the blocks. I need help as to how to make our character carry blocks that are in front of him, provided that the blocks that don't have anything on top of it. This has been confusing me for a week now, and any help would be highly appreciated. :D
I fondly remember my first AS2 game. The best approach is probably an object oriented approach, as I will explain.
In AS2, there is a hittest method automatically built into objects. There is a good tutorial on Kirupa here:
http://www.kirupa.com/developer/actionscript/hittest.htm
also
http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00001314.html
First you'll want to generate your boxes using a Box class. Your class would need to look something like the following:
//Box.as pseudo-code
class Box {
var x_pos:Number;
var y_pos:Number;
var attachedToPlayer:Boolean;
function Box(_x:Number, _y:Number) {
this.x_pos = _x;
this.y_pos = _y;
}
//other code here
}
See this tutorial on how to attach a class to an object in the library:
http://www.articlesbase.com/videos/5min/86620312
To create a new Box, you'd then use something like
box1 = new Box(100,200);
// creates a box at position 100x,200y
However, you'll also want to store the blocks you want to pickup into some sort of array so you can loop through them. See http://www.tech-recipes.com/rx/1383/flash-actionscript-create-an-array-of-objects-from-a-unique-class/
Example:
//somewhere near the top of your main method, or whereever your main game loop is running from - note Box.as would need to be in the same folder
import Box;
//...then, somewhere before your game loop
//create an array to hold the objects
var boxArray:Array = new Array();
//create loop with i as the counter
for (var i=0; i<4; i++)
{
var _x:Number = 100 + i;
var _y:Number = 100 + i;
//create Box object
var box:Box = new Box();
//assign text to the first variable.
//push the object into the array
boxArray.push(box);
}
Similarly, you would need a class for your player, and to create a new Player object at the start of your game, e.g.
var player = new Player(0,0);
You could then run a hittest method for your player against the blocks in your array for the main game loop (i.e. the loop that updates your player's position and other game properties). There are probably more efficient ways of doing this, e.g. only looping for the blocks that are currently on the screen.
Once your array has been created, use a foreach loop to run a hittest against your player in your game's main loop, e.g.
//assuming you have an array called 'boxArray' and player object called 'player'
for(var box in boxArray){
if (player.hittest(box)) {
player.attachObjectMethod(box);
}
}
This is basically pseudo-code for "for every box that we have entered into the array, check if the player is touching the box. If the box is touching, use the box as the argument for a method in the player class (which I have arbitrarily called attachObjectMethod)".
In attachObjectMethod, you could then define some sort of behavior for attaching the box to the player. For example, you could create a get and set method(s) for the x and y position of your boxes inside the box class, along with a boolean called something useful like attachedToPlayer. When attachObjectMethod was called, it would set the box's boolean, e.g. in the Player class
//include Box.as at the top of the file
import Box;
//other methods, e.g. constructor
//somewhere is the Player.as class/file
public function attachObjectMethod (box:Box) {
box.setattachedToPlayer(true);
//you could also update fields on the player, but for now this is all we need
}
Now the attachedToPlayer boolean of the box the player has collided with would be true. Back in our game loop, we would then modify our loop to update the position of the boxes:
//assuming you have an array called 'boxArray' and player object called 'player'
for(var box in boxArray){
if (player.hittest(box)) {
player.attachObjectMethod(box);
}
box.updatePosition(player.get_Xpos, player.get_Ypos);
}
In our Box class, we now need to define 'updatePosition':
//Box.as pseudo-code
class Box {
var x_pos:Number;
var y_pos:Number;
var attachedToPlayer:Boolean;
function Box(box_x:Number, box_y:Number) {
this.x_pos = box_x;
this.y_pos = box_y;
}
public function updatePosition(_x:Number, _y:Number) {
if (this.attachedToPlayer) {
this.x_pos = _x;
this.y_pos = _y;
}
}
//other code here
}
As you can see we can pass the player's position, and update the box's position if the attachedToPlayer boolean has been set. Finally, we add a move method to the box:
public function move() {
if (this.attachedToPlayer) {
this._x = x_pos;
this._y = y_pos;
}
}
Examples of updating position:
http://www.swinburne.edu.au/design/tutorials/P-flash/T-How-to-smoothly-slide-objects-around-in-Flash/ID-17/
Finally, to make it all work we need to call the move method in the game loop:
//assuming you have an array called 'boxArray' and player object called 'player'
for(var box in boxArray){
if (player.hittest(box)) {
player.attachObjectMethod(box);
}
box.updatePosition(player.get_Xpos, player.get_Ypos);
box.move();
}
You have also specified that the blocks should only move with the player if they have nothing on top of them. When you call your attachedToPlayer method, you would also need to run a foreach loop inside the method between the box and the objects that might sit on top of the box. You should now have a fair idea from the above code how to do this.
I appreciate that this is quite a lengthy answer, and I haven't had an opportunity to test all the code (in fact I'm fairly positive I made a mistake somewhere) - don't hesitate to ask questions. My other advice is to understand the concepts thoroughly, and then write your own code one bit at a time.
Good luck!
The way I would do this is to design an individual hit test for each block he will be picking up, then code for the hit test to play a frame within the sprite's timeline of him carrying a block, and to play a frame within the block to be picked up's timeline of the block no longer at rest (disappeared?).
Good Luck if you're confused about what I've said just ask a little more about it and I'll try to help you if I can.