I made this game with Python+Pygame, but it was slow, so I tried it with C# and its forms. It's even slower! I get only 20fps on a i5 radeon 6770m NTB millions of times faster than hardware I want this game to run and I haven't even finished the game yet, it's only rendering a map. It's a remake of a game that was run with slow several MHz processors. It's map containes 400x200 tiles and camera shows only 79*79. I also installed unity 4.5; is it worth learning it, would it bring me significant perfomance increase - if someone knows it how would I do a map consisting of 600*400 tiles randomly either dark tile or bright; it has to be "collidable"? Or I doing sth wrong in forms?
public void render()
{
Bitmap frame = new Bitmap(Game.CANVAS_WIDTH, Game.CANVAS_HEIGHT);
Graphics frameGraphics = Graphics.FromImage(frame);
TextureID[,] textures = Background.Blocks;
while (true)
{
//frameGraphics.FillRectangle(new SolidBrush(Color.Aqua), 0, 0, Game.CANVAS_WIDTH, Game.CANVAS_HEIGHT);
for (int x = 0; x < Game.AR_WIDTH; x++)
{
int xx = x * Game.TILE_SIDE - game.green_tank_pos[0] + Game.DIFF;
if (xx < 0) continue;
if (xx > Game.CANVAS_WIDTH) break;
for (int y = 0; y < Game.AR_HEIGHT; y++)
{
int yy = y * Game.TILE_SIDE - game.green_tank_pos[1] + Game.DIFF;
if (yy < 0) continue;
if (yy > Game.CANVAS_HEIGHT) continue;
switch(textures[x,y])
{
case TextureID.dark:
frameGraphics.DrawImage(tex_dark_gnd, xx, yy);
break;
case TextureID.bright:
frameGraphics.DrawImage(tex_bright_gnd, x * Game.TILE_SIDE - game.green_tank_pos[0] + Game.DIFF, y * Game.TILE_SIDE - game.green_tank_pos[1] + Game.DIFF);
break;
}
}
}
frameGraphics.DrawImage(tex_green_tank, Game.DIFF, Game.DIFF);
drawHandle.DrawImage(frame, 0, 0);
WinForms is a terrible platform for a game, even a 2D one. It was intended for line-of-business applications, and includes little to no hardware acceleration.
Plus the technology is just hard to write a game in, let alone an efficient one. You might get away with using WPF for a very simple game, but you will really want to learn XNA, MonoGame, Unity or some other actual game platform that can take advantage of DirectX (WPF does this as well, btw).
There are ways to get better performance drawing in winforms but for a game it's best to go with tools that better fit the job. (don't use a screwdriver to hit a nail).
The newer versions of Unity3D have built-in 2D tools. I'm personally building a 2D game in it and would highly recommend it.
EDIT: Removed my mention of XNA - I wasn't aware it was "dead".
Creating tiles in Unity3D can be done several ways
Place sprites into the scene manually. Their locations and settings will be saved along with the scene.
Place the sprites within the scene manually and create a prefab out of it. Thus, that set of tiles can be reused.
Create a prefab of a single tile, and instantiate multiples of that prefab in a Behavior that is attached somewhere in a scene.
As for the rendering Unity3D will take care of that for you.
EDIT2: I created a short Unity3D behavior that you can attach somewhere inside your scene. This uses the 3rd approach I outlined above and it will pick randomly from a set of prefabs applied. This script assumes randomly picking from tiles and that the width of the tiles are 1 game unit wide/high.
Attach the behavior to something in the scene (like the main camera)
Create your tiles as prefabs
Drag the prefabs in the editor into the "Tile Prefabs" array on the behavior you've attached.
Set the "Tiles High" to 400 and "Tiles Wide" to 600
Set TileMapTopLeft to the top-left position you want it to start.
Run your scene.
Here's the behavior:
using UnityEngine;
class TileCreator : MonoBehaviour
{
private static System.Random rng = new System.Random();
public GameObject[] TilePrefabs;
public int TilesWide;
public int TilesHigh;
public Vector3 TileMapTopLeft;
void Start()
{
for (int x = 0; x < TilesWide; x++)
{
for (int y = 0; y < TilesHigh; y++)
{
Instantiate(TilePrefabs[rng.Next(TilePrefabs.Length)], new Vector3(x + TileMapTopLeft.x, y + TileMapTopLeft.y, TileMapTopLeft.z), Quaternion.identity);
}
}
}
}
You'll probably need to look up a tutorial on how to import resources and create prefabs but this script should get you in the right direction.
Related
Okay so I've rolled this around in my brain, and while I would probably know what code to use if I could figure it out, I just can't decide what the best way to implement this is.
Basically, say I have a vector grid. This grid could have any number of blockaded areas that the item I am pushing around can't go to. The pushing of the object is done by an interact button using the "new" inputsystem package. I could have a dynamic rigidbody and let it be pushed around that way, but I want to use the interact button I have, in part because the first puzzle of this type is used in a tutorial to teach the player the game's commands.
Without knowing where the blockades are going to be, I'm not sure how to tell where the object can move at any given time. My current thought is use colliders, and keep them far enough away that there isn't an actual collision since I don't think I can place them perfectly enough to get collisions at the right times, and throw out raycasts at a short range to detect these colliders. (This gives me more "give" when placing the colliders.)
Does this sound right? Is there a more efficient way to do it? Even if it requires more complex code, I am interested in feedback because I want to learn "good coding" and how to do things in the best, most efficient way possible, rather than spaghetti coding my way through it.
The most efficient way? Forget Colliders. Basic old-school game dev skill: Working with arrays. Create a 2D array of boolean values, or integers set to 0 or 1 representing OPEN or CLOSED.
using UnityEngine;
public class TestObstacleGrid : MonoBehaviour
{
const int GRID_WIDTH = 8; //if you change this, you'll need to match the size of the grid, below.
int[,] grid = {
// -> +x
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,1,1,1,1,1},
{0,0,0,1,0,1,1,1},
{0,0,0,1,0,1,1,1},
{0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0}
//|
//V
//+z
};
const int OPEN = 0;
const int CLOSED = 1;
public Vector3 playerPos = Vector3.zero; //give player a start position via inspector.
//You need to drag/drop something like a Cube GameObject into here, via the inspector.
public GameObject playerGameObject;
Vector3 playerOffset = new Vector3(0.5f, 0.5f, 0.5f);
Texture2D tex;
void Start()
{
tex = new Texture2D(8, 8, TextureFormat.ARGB32, false);
tex.filterMode = FilterMode.Point; //makes texture crisp, not blurry
//assumes you are using a plane, not a quad.
this.transform.position = new Vector3(GRID_WIDTH / 2, 0, GRID_WIDTH / 2); //shift by half
this.transform.localScale = new Vector3(-GRID_WIDTH / 10f, 1f, -GRID_WIDTH / 10f); //Unity Planes are 10 units wide.
MeshRenderer renderer = this.GetComponent<MeshRenderer>();
renderer.material.mainTexture = tex;
playerGameObject.transform.position = playerPos;
//Only needs to be called once in Start() or Awake(), if grid doesn't change.
RenderGridToTexture();
}
void Update()
{
Vector3 motion = Vector3.zero;
if (Input.GetKeyDown(KeyCode.W))
motion += Vector3.forward;
if (Input.GetKeyDown(KeyCode.S))
motion += Vector3.back;
if (Input.GetKeyDown(KeyCode.A))
motion += Vector3.left;
if (Input.GetKeyDown(KeyCode.D))
motion += Vector3.right;
Vector3 proposedPos = playerPos + motion;
//prevent player leaving the grid.
proposedPos.x = proposedPos.x < 0 ? 0 : proposedPos.x;
proposedPos.x = proposedPos.x > GRID_WIDTH - 1 ? GRID_WIDTH - 1 : proposedPos.x;
proposedPos.z = proposedPos.z < 0 ? 0 : proposedPos.z;
proposedPos.z = proposedPos.z > GRID_WIDTH - 1 ? GRID_WIDTH - 1 : proposedPos.z;
if (grid[(int)proposedPos.z, (int)proposedPos.x] == CLOSED)
{
proposedPos = playerPos; //reset the proposed to the current position (stay there).
Debug.Log("Invalid attempted move from " + playerPos +
" to " + proposedPos);
}
else //OPEN
playerPos = proposedPos;
playerGameObject.transform.position = playerPos + playerOffset; //offset shifts the cube so it looks right.
//Uncomment this if the grid can change during play.
//RenderGridToTexture();
}
void RenderGridToTexture()
{
for (int z = 0; z < GRID_WIDTH; z++)
for (int x = 0; x < GRID_WIDTH; x++)
tex.SetPixel(x, z,
grid[z, x] == 1 ? Color.red : Color.black );
tex.Apply();
}
}
Create an empty GameObject, and attach to this script along with a MeshRenderer and MeshFilter; for the MeshFilter, give it a Plane mesh. Create a Cube GameObject and drag it onto the script's playerGameObject field in the inspector. The script will do the rest.
Using the keys, your player moves one full square at a time, but you'll want smooth movement in the end. That's fine; this is only a basis for your game. Later on, when you want smooth movement, you can figure it out using Mathf.Lerp() with coroutines to move your player smoothly from one space to another (as well as the object your player is pushing).
You'll need to figure out pushing - the principle is the same as already used here to check for collisions: Look at the grid and see if a non-zero value is there. Then look in front of the object to see if there is an obstacle. Use a different number in the grid, like 2, to represent a pushable object.
That's the best I can do in short form, hope it's enough. Adios and good luck.
First of all, please understand that sentences may not be smooth using a translator.
I'm going to combine the two textures and create them into one texture.
Example) 1. image__ 2. image__ 3. result__
A simple combination of two textures is not a problem.
The problem is that translate, rotate and scale should be applied to one texture and merged, but I can't think of a way. I'd appreciate it if you could help me.
here my code :
Texture2D CombineTexutes(Texture2D _textureA, Texture2D _textureB, int startX = 0, int startY = 0)
{
//Create new textures
Texture2D textureResult = new Texture2D(_textureA.width, _textureA.height);
//create clone form texture
textureResult.SetPixels(_textureA.GetPixels());
//Now copy texture B in texutre A
for (int x = startX; x < _textureB.width + startX; x++)
{
for (int y = startY; y < _textureB.height + startY; y++)
{
Color c = _textureB.GetPixel(x - startX, y - startY);
if (c.a > 0.0f) //Is not transparent
{
//Copy pixel colot in TexturaA
textureResult.SetPixel(x, y, c);
}
}
}
//Apply colors
textureResult.Apply();
return textureResult;
}
This is relatively tricky with pixel transformations. You would have to read up on various algorithms for every of those tasks.
A much easier solution would be to use game objects with sprite renderers and then render everything onto a "render texture" (if you really need a texture).
Game objects with a sprite renderer can be easily rotated, scaled etc.
Your result image does look as if it was actually deforming the sprite though, not just scaled in a direction.
If that's your goal, then I'd recommend looking at the Animation 2D package. That allows you to do near anything with sprites.
Recently I've been trying to make a 2D game in XNA, but I seem to have hit a dead end. No mater where I look on the internet, I can't seem to find any examples of how to make two objects collide in XNA. I know perfectly well about how to detect if something is colliding, just not what to put between the if statement.
In my case it's trying to make a player not pass through any blocks on the screen. I've tried several ways, but none of them seems to work. :(
Thanks in advance!
So, this is sort of a dirty solution, but you could save the position of where you are, move, and then if you collide, revert back to the original position.
Here's a code example, providing your method to check for collision is called doesCollide() and your method to do the game logic and move the player accordingly is called update().
Also, getX(), getY(), setX() and setY() are just methods to get/set the coordinates of the player.
int lastX = player.getX();
int lastY = player.getY();
player.update();
if(player.doesCollide()){
player.setX(lastX);
player.setY(lastY);
}
This method especially faces problems, when the player is moving at a high speed, because then the player might glitch through obstacles or not be able to get close to walls.
Here's a solution that fixes that, but it is even dirtier:
int lastX = player.getX();
int lastY = player.getY();
player.update();
int moveX = player.getX() - lastX;
int moveY = player.getY() - lastY;
player.setX(lastX);
player.setY(lastY); //we're basically figuring out where the player would move
int ratio = moveY / moveX;
for(int i = 0; i < moveX; i++){ //we move the player pixel by pixel
player.setX(lastX + i);
player.setY(lastY + (int) (i * ratio));
if(player.doesCollide()){ //we revert the last pixel move
player.setX(lastX + i - 1);
player.setY(lastY + (int) ((i - 1) * ratio));
}
}
I have a real brain teaser right here and I can't figure out what I am doing wrong since the code looks absolutely normal.
Little background information:
Since I've started working in the XNA/MonoGame environment for about 2 days now I wanted to start off with something easy. I'm currently building an astroid defender like game, and I'm working on a collision check. Before I even got to the collision, I found that I got a rather odd null reference while im using a for loop to loop through all enemies inside a list. The list however is in another class.
Upon startup I initizalize a SpawnManager class that immediately creates 20 enemies when the class has been made like so:
class SpawnManager
{
//Draw
ContentManager myContentManager;
//Enemies
public List<Enemy> enemy = new List<Enemy>();
public void LoadContent(ContentManager theContentManager)
{
myContentManager = theContentManager;
for (int i = 0; i < 20; i++)
{
Enemy newEnemy = new Enemy(new Vector2((800 / (20 + 1) * i + 16.5f), 20), 500, 50, 25, 10, 2500, 100, 1, 3, 2, false);
newEnemy.LoadContent(myContentManager);
enemy.Add(newEnemy);
}
}
Then whenever I fire a bullet with my spaceship, it will create a bulletclass like so:
public class Bullet : PlayerScript
{
//Enemy enemy;
SpawnManager spawnManager;
PlayerScript playerScript;
private Texture2D _bullet;
private Rectangle _hitBox;
public float xPos = 0;
public float yPos = 0;
public bool isActive = false;
public void LoadContent(ContentManager theContentManager)
{
_bullet = theContentManager.Load<Texture2D>("Graphics/citem");
_hitBox = new Rectangle(0, 0, _bullet.Width, _bullet.Height);
}
public void Update(GameTime gameTime)
{
//Movement.
yPos -= 13.5f;
//Collision.
for (int i = 0; i < spawnManager.enemy.Count; i++)
{
//Console.WriteLine(spawnManager.enemy.Count);
}
}
When while im holding space to fire bullets, I immediately get a null reference error, hightlighting the for loop part. I've read some posts on the internet saying that the Enemy List might be null or empty, but even if that is the case, that shouldn't really matter as my code structure goes as follows:
Game1 Initialize -> add SpawnManager -> add Enemies + push them in the Enemy list.
So when the enemies are on the screen, they are basically already added to the Enemy list and the List shouldn't be null or empty.
Also, might there be an easier or more efficient way to check for collision? Because everytime I'm creating a bullet, a bullet.Update script will start looping through all enemies like a madman until it reaches an enemy, checking which enemy it is, applying changes to it and adding itself to a pool. (Ofcourse when its in the pool I wont update the Bullet script). Since this astroid defender is more like a little bullet hell, shooting 100 bullets per few seconds, will cause a lot of looping in total. I just came over to XNA / MonoGame C# after spending 1.5 years in Unity, and in Unity everything seems so simple, OnCollisionEnter ftw! :)
Still, I'm pretty sure its a total ID-10-T error on my side, but I just cant wrap my head around it, any help would be gratefully appreciated :)
P.S: If anything is stated unclear, I'd be glad to edit this question!
I'm sorry if question title was unclear, but with my cheap english, I cant find a way to ask it clearly.
But I can explain it in long way.
So I have realized if I design my world(and with world, I mean ENTIRE game, it will be one level) 10.000x10.000... it will be very enough, other than few another sprite(and I mean like 4 or 5 with maximum of 50x50, nothing big.)
So I thought, why dont I make my entire map as 10.000x10.000(or lets say tons of 512x512) picture ?
But I have one question, there is few things you can "interact". they will(with they, I mean the stuff that is in my "world.jpg") be always stay at same place, but player(which is actually a sprite as you know) will move, therefore my 10.000x10.000 will "move".
So look at picture below, there is black dot which is "player" and red dot, which is lets say, a door.
and world is always centered to black dot unless he goes to end of the world. as you can see, (look at picture part 1 and part 2) when he moves a little bit to east, red dot looks moved. but I just moved my 10.000x10.000 image. Thats what I meant with the stuff on 10kx10k pic will move.
Anyway, but as you can see in last part of pic, when he goes near red dot, I want to my "action"
How to do it ?
-part below is not really related to main question
Is it useful to use 10kx10 pic instead of another sprites appearing on world when he moves ? but If I want to do that, not just I will check if he is nearby, but I will also check his point to realize if I should or shouldnt show him sprite.
Will it be more useful if I show my stuff when he comes to coordinates I want, or is using one big picture is OK ?
Thanks.
I would suggest a structure of the map somewhat like this..
public class Map
{
public MapPoint[,] mapPoints; //the map
public Player player; //the player/user object
public Vector2 DrawHeroPosition;
//where at the screen the player is going to be drawn
public Vector2 RangeStart;
//what part of the map who is going to be drawn
public int SizeX; //number of mapPoints the screen can contain at one time
public int SizeY; //number of mapPoints the screen can contain at one time
//MapPoint represents a specific 512x512 point (mapPoint) its position at
//the map but also includes the sprite that is going to be drawn and objects
//that the player can interact with at that place (like the door)
//the player object includes reference to where in the world it is place
public Map(ContentManager theContentManager, int x, int y)
{
MapSizeX = x;
MapSizeY = y;
int ScreenSizeX = 9;
int ScreenSizeY = 9;
mapPoints = new MapPoint[MapSizeX , MapSizeY];
//ad code for generating/creating map...
//important that you store the MapPoints position inside each mapPoint
player = new Player(mapPoints[0,0]); //crate a player who knows where he is
}
public void Update()
{
//in the update method you do a lot of things like movement and so
//set what part of the map the game should draw if the game for example
//can show 9x9 512points at a single time
//give range value from the players position
RangeStart.X = player.PositionX;
//test if the maps position is in the left corner of the map
//if it is draw the map from the start..(RangeStart.X = 0)
if (player.PositionX - (ScreenSizeX / 2) < 0) { RangeStart.X = 0; }
//if not draw the hero in the mitle of the screen
else
{
RangeStart.X = player.PositionX - (ScreenSizeX / 2);
}
//if the hero is in the right corer, fix his position
while (RangeStart.X + ScreenSizeX > MapSizeX)
{
RangeStart.X--;
}
//the same thing for the Y axle
RangeStart.Y = player.PositionY;
if (player.PositionY - (ScreenSizeY / 2) < 0) { RangeStart.Y = 0; }
else
{
RangeStart.Y = player.PositionY - (ScreenSizeY / 2);
}
while (RangeStart.Y + ScreenSizeY > MapSizeY)
{
RangeStart.Y--;
}
//time to set the position of the hero...
//he works like the opposite of the range, if you move what part of the map
//you draw you dont change the heros draw position, if you dont move the range
//you have to move the hero to create the illusion of "moment"
//if you are in the left part you have to move the heros draw position..
if (player.PositionX - (ScreenSizeX / 2) < 0)
{ DrawHeroPosition.X = player.PositionX; }
//if you are in the right part
else if (player.PositionX+1 > MapSizeX - (ScreenSizeX / 2))
{
DrawHeroPosition.X = player.PositionX - (MapSizeX - ScreenSizeX);
}
//if you aint in a corner, just place the hero in the middle of the map
else
{
DrawHeroPosition.X = (ScreenSizeX / 2);
}
//the same thing for Y
if (player.PositionY - (ScreenSizeY / 2) < 0)
{ DrawHeroPosition.Y = player.PositionY; }
else if (player.PositionY+1 > MapSizeY - (ScreenSizeY / 2))
{
DrawHeroPosition.Y = player.PositionY - (MapSizeY - ScreenSizeY);
}
else
{
DrawHeroPosition.Y = (ScreenSizeY / 2);
}
}
public void Draw()
{
int x = (int)RangeStart.X;
int y = (int)RangeStart.Y;
for(int counterX = 0; x < ((MapSizeX)); x++, counterX++)
{
for (int counterY = 0; y < (MapSizeY); y++, counterY++)
{
if (mapPoints[x, y] != null)
{
mapPoints[x, y].Draw(spriteBatch, mapPoints[counterX,counterY].positonInMatrix);
//mapPoints[counterX,counterY] = where to draw
//mapPoints[x, y] = what to draw
}
}
y = (int)RangeStart.Y;
}
}
}
how i draw inside the MapPoint Class...
public void Draw(SpriteBatch theSpriteBatch, Vector2 positonOnScreen)
{
positonOnScreen = new Vector2(positonOnScreen.X * base.Scale * 16,
positonOnScreen.Y * base.Scale * 16);
//base.Scale is just a variable for have the ability to zoom in/out
//16 represents the original size of the picture (16x16 pixels)
theSpriteBatch.Draw(mSpriteTexture, new Rectangle((int)positonOnScreen.X,
(int)(positonOnScreen.Y), 64, 64),new Rectangle(0, 0, 16, 16), Color.White);
}
If you are asking for collision detection within a radius of your red dot. You can simply use the following test (pseudocode, I don't write C# :-)
if( (player.GetPosition() - point.GetPosition()).length() < radius )
{ /* Do code here */ }
This will detect if your player is within a certain radius of your dot, you can then perform whatever action you wish.
Hope this helps! :)
Ok, from what I understand of your question, you have a large image which contains different objects you want your player to interact with, yes? By which I mean, the image file itself has doors or hills or other things which the player would interact with.
This is a bad idea, honestly, so I hope I misunderstood. It is far better to have your background image just be something generic and make all interactive objects classes within your game. If you do this, then you can have your object classes contain behavior to intersect with each other either based on their distance (circle collision) or based on a bounding box you define for them.
Circle Collision:
if (Math.Abs(Vector2.Distance(player.Position - obj.Position)) < player.Radius + obj.Radius)
//do the action
Rectangle Collision:
if (player.Bounds.Intersects(obj.Bounds))
//do the action
Also, if you are planning on making a 10,000 x 10,000 pixel image, understand that the XNA Content Pipeline will not import an image greater than 4,000 pixels to a side.
If you are set on having the player interact with pixels in the background of the image, you can either make an array of interactive object locations manually or you can use the Texture2D.GetData() method to load in the colors of every single pixel in the image for processing-- but be aware that this will take a long time, especially for large or numerous textures.