简体   繁体   中英

Game unplayable because of FPS drops

Am I the only one experiencing this? I have nothing but a little, tiny, game, but it's close to unplayable now because of FPS drops.

Here's my code, if anyone is wondering:

Game1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;

namespace Innovationally
{
enum GameState
{
    TITLESCREEN,
    HELPSCREEN,
    PLAYING,
    WON,
    LOST
}
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    GameState gameState = GameState.PLAYING;

    //PLAYER STATS
    public static Player player;
    KeyboardState currentKeyboardState;
    KeyboardState previousKeyboardState;
    float playerMoveSpeed;

    //COLLISION STATS
    Rectangle kollision;
    int bHit;

    //LEVEL STATS
    int level_number = 0;
    int loadlevel = 0;
    Texture2D hud, level0, level1, level2, level3, level4, level5;
    Vector2 levelPos;

    //ROOM STATS
    List<int> tile_life = new List<int>();
    Texture2D tile_gfx, stairsUp, stairsDown;
    List<Vector2> tile_position = new List<Vector2>();
    List<int> tile_type = new List<int>();
    List<int> tile_elev = new List<int>();
    int antlabb = 0;
    int antvapen = 0;
    int antpolis = 0;
    int antwavers = 0;
    int researchSpan;

    //MISC
    SpriteFont font;
    Loot loot;

    //GAMEPLAY STATS
    TimeSpan timeElapsed;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        graphics.PreferredBackBufferHeight = 800;
        graphics.PreferredBackBufferWidth = 900;
    } 
    protected override void Initialize()
    {
        player = new Player();
        playerMoveSpeed = 4.0f;
        levelPos.X = 0;
        levelPos.Y = 0;
        loot = new Loot();
        researchSpan = 120;
        base.Initialize();
    }
    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);

        Vector2 playerPosition = new Vector2(430, 450);
        //LEVEL STATS
        LaddaLevel(level_number);
        level0 = Content.Load<Texture2D>("level0");
        level1 = Content.Load<Texture2D>("level1");
        level2 = Content.Load<Texture2D>("level2");
        level3 = Content.Load<Texture2D>("level3");
        level4 = Content.Load<Texture2D>("level4");
        level5 = Content.Load<Texture2D>("level5");
        hud = Content.Load<Texture2D>("hud");

        //ROOM STATS
        tile_gfx = Content.Load<Texture2D>("tile");
        stairsUp = Content.Load<Texture2D>("stairsUp");
        stairsDown = Content.Load<Texture2D>("stairsDown");

        font = Content.Load<SpriteFont>("SpriteFont1");
        player.Initialize(Content.Load<Texture2D>("Leftplayer"), playerPosition);

        //SOMEONE STOP THE MUSIC, MUSIC.
        MediaPlayer.Volume = 0.5f;
        MediaPlayer.IsRepeating = true;
        MediaPlayer.Play(Content.Load<Song>("barn-beat"));
    }
    public void LaddaLevel(int nummer)
    {
        StreamReader SR = new StreamReader(nummer.ToString());

        string bana = SR.ReadToEnd();
        SR.Close();
        int temp_positionY = 0;
        int temp_positionX = 0;

        tile_position.Clear(); 
        tile_type.Clear();    
        tile_life.Clear(); 

        for (int i = 0; i < bana.Length; i++)
        {
            switch (bana[i])
            {
                case ' ':
                    temp_positionX++;
                    break;
                case '0':
                    tile_life.Add(loot.myRnd.Next(8));
                    tile_position.Add(new Vector2((temp_positionX * 100), (temp_positionY * 100)));
                    temp_positionX++;
                    tile_type.Add(int.Parse(bana[i].ToString()));
                    break;
                case '8': 
                    tile_position.Add(new Vector2((temp_positionX * 100), (temp_positionY * 100)));
                    temp_positionX++;
                    tile_type.Add(int.Parse(bana[i].ToString()));
                    tile_life.Add(8);
                    break;
                case '9': 
                    tile_position.Add(new Vector2((temp_positionX * 100), (temp_positionY * 100)));
                    temp_positionX++;
                    tile_type.Add(int.Parse(bana[i].ToString()));
                    tile_life.Add(9);
                    break;
                case '\n':
                    temp_positionY++;
                    temp_positionX = 0;
                    break;
            }
        }
    }
    protected override void UnloadContent()
    {
    }
    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();
        previousKeyboardState = currentKeyboardState;
        currentKeyboardState = Keyboard.GetState();
        if (currentKeyboardState.IsKeyDown(Keys.Escape) && previousKeyboardState.IsKeyUp(Keys.Escape))
            this.Exit();

        switch (gameState)
        {
            case GameState.TITLESCREEN:
                if (currentKeyboardState.IsKeyDown(Keys.S) && previousKeyboardState.IsKeyUp(Keys.S))
                    gameState = GameState.PLAYING;
                if (currentKeyboardState.IsKeyDown(Keys.H) && previousKeyboardState.IsKeyUp(Keys.H))
                    gameState = GameState.HELPSCREEN;
                break;
            case GameState.HELPSCREEN:
                if (currentKeyboardState.IsKeyDown(Keys.B) && previousKeyboardState.IsKeyUp(Keys.B))
                    gameState = GameState.TITLESCREEN;
                break;
            case GameState.PLAYING:
                timeElapsed += gameTime.ElapsedGameTime;
                UpdatePlayer(gameTime);
                UpdateResearchCenters(gameTime);
                UpdateCollisions(gameTime);
                break;
            case GameState.LOST:
                if (currentKeyboardState.IsKeyDown(Keys.S) && previousKeyboardState.IsKeyUp(Keys.S))
                {
                    loadlevel = 0;
                    gameState = GameState.PLAYING;
                }
                break;
        }
        base.Update(gameTime);
    }
    private void UpdatePlayer(GameTime gameTime)
    {

        if (currentKeyboardState.IsKeyDown(Keys.Left))
        {
            player.angle = (float)Math.PI * 1.5f;
            player.Position.X -= playerMoveSpeed;
        }
        if (currentKeyboardState.IsKeyDown(Keys.Right))
        {
            player.angle = (float)Math.PI / 2;
            player.Position.X += playerMoveSpeed;
        }
        if (currentKeyboardState.IsKeyDown(Keys.Up))
        {
            player.angle = (float)Math.PI * 2;
            player.Position.Y -= playerMoveSpeed;
        }
        if (currentKeyboardState.IsKeyDown(Keys.Down))
        {
            player.angle = (float)Math.PI;
            player.Position.Y += playerMoveSpeed;
        }
        if (player.health <= 0)
            gameState = GameState.LOST;
    }
    public void UpdateResearchCenters(GameTime gameTime)
    {
        researchSpan -= gameTime.ElapsedGameTime.Seconds;

        if (researchSpan <= 0)
        {
            loot.RandomResearch();
            researchSpan = 120;
        }
    }
    public void UpdateCollisions(GameTime gameTime)
    {
        Rectangle playerBox = new Rectangle((int)player.Position.X - 20, (int)player.Position.Y - 20, 40, 37);
        Rectangle levelBox = new Rectangle(0, 0, 900, 800);

        for (int i = 0; i < tile_position.Count; i++)
        {
            Rectangle tileBox = new Rectangle((int)tile_position[i].X, (int)tile_position[i].Y, 100, 100);
            if (playerBox.Intersects(tileBox))
            {
                if (tile_life[i] <= 9)
                {
                    if (tile_life[i] == 9 && currentKeyboardState.IsKeyDown(Keys.Space)) 
                    {
                        loadlevel += 1;
                        LaddaLevel(loadlevel);
                    }
                    else if (tile_life[i] == 8 && currentKeyboardState.IsKeyDown(Keys.Space))
                    {
                        loadlevel -= 1;
                        LaddaLevel(loadlevel);
                    }
                    else if (tile_life[i] == 7 && currentKeyboardState.IsKeyDown(Keys.Space))
                    {
                        loot.RandomLoot();
                        tile_life[i] = 70; 
                    }
                    else if ((tile_life[i] == 6 || tile_life[i] == 60) && currentKeyboardState.IsKeyDown(Keys.Space) && (player.mvgelever >= 1 || player.problemelever >= 1 || player.normalaelever >= 1))
                    {
                        if (player.mvgelever >= 1)
                        {
                            player.mvgelever -= 1;
                            tile_elev.Add(1);
                        }
                        else if (player.normalaelever >= 1)
                        {
                            player.normalaelever -= 1;
                            tile_elev.Add(2);
                        }
                        else if (player.problemelever >= 1)
                        {
                            player.problemelever -= 1;
                            tile_elev.Add(3);
                        }
                        tile_life[i] = 60;
                        antlabb += 1;

                    }
                    if (tile_life[i] == 60 && currentKeyboardState.IsKeyDown(Keys.Space))
                        {
                            antlabb -= 1;
                            if (tile_elev.Contains(1))
                                player.mvgelever += 1;
                            if (tile_elev.Contains(2))
                                player.normalaelever += 1;
                            if (tile_elev.Contains(3))
                                player.problemelever += 1;
                            tile_life[i] = 6;
                        }
                }
            }
        }
        //Överlappar vi?
        kollision = Intersection(playerBox, levelBox);

        if (kollision.Width > 0 && kollision.Height > 0)
        {
            Rectangle r1 = Normalize(playerBox, kollision);
            Rectangle r2 = Normalize(levelBox, kollision);
            if (loadlevel == 0)
                bHit = TestCollision(player.PlayerTexture, r1, level0, r2);
            if (loadlevel == 1)
                bHit = TestCollision(player.PlayerTexture, r1, level1, r2);
            if (loadlevel == 2)
                bHit = TestCollision(player.PlayerTexture, r1, level2, r2);
            if (loadlevel == 3)
                bHit = TestCollision(player.PlayerTexture, r1, level3, r2);
            if (loadlevel == 4)
                bHit = TestCollision(player.PlayerTexture, r1, level4, r2);
            if (loadlevel == 5)
                bHit = TestCollision(player.PlayerTexture, r1, level5, r2);
        }
        else
        {
            bHit = 0;
        }

        if (bHit == 1 || bHit == 2)
        {
            if (player.angle == (float)Math.PI)
                player.Position.Y -= playerMoveSpeed;
            if (player.angle == (float)Math.PI * 2)
                player.Position.Y += playerMoveSpeed;
            if (player.angle == (float)Math.PI / 2)
                player.Position.X -= playerMoveSpeed;
            if (player.angle == (float)Math.PI * 1.5f)
                player.Position.X += playerMoveSpeed;
        }
    }
    public static Rectangle Intersection(Rectangle r1, Rectangle r2)
    {
        int x1 = Math.Max(r1.Left, r2.Left);
        int y1 = Math.Max(r1.Top, r2.Top);
        int x2 = Math.Min(r1.Right, r2.Right);
        int y2 = Math.Min(r1.Bottom, r2.Bottom);

        if ((x2 >= x1) && (y2 >= y1))
        {
            return new Rectangle(x1, y1, x2 - x1, y2 - y1);
        }
        return Rectangle.Empty;
    }

    public static Rectangle Normalize(Rectangle reference, Rectangle overlap)
    {
        //Räkna ut en rektangel som kan användas relativt till referensrektangeln
        return new Rectangle(
          overlap.X - reference.X,
          overlap.Y - reference.Y,
          overlap.Width,
          overlap.Height);
    }

    public static int TestCollision(Texture2D t1, Rectangle r1, Texture2D t2, Rectangle r2)
    {
        //Beräkna hur många pixlar som finns i området som ska undersökas
        int pixelCount = r1.Width * r1.Height;
        uint[] texture1Pixels = new uint[pixelCount];
        uint[] texture2Pixels = new uint[pixelCount];

        //Kopiera ut pixlarna från båda områdena
        t1.GetData(0, r1, texture1Pixels, 0, pixelCount);
        t2.GetData(0, r2, texture2Pixels, 0, pixelCount);

        //Jämför om vi har några pixlar som överlappar varandra i områdena
        for (int i = 0; i < pixelCount; ++i)
        {
            if (((texture1Pixels[i] & 0xff000000) > 0) && (texture2Pixels[i] == 0xffC3C3C3))
            {
                return 1;
            }
            if (((texture1Pixels[i] & 0xff000000) > 0) && (texture2Pixels[i] == 0xff000000))
            {
                return 2;
            }
            if (((texture1Pixels[i] & 0xff000000) > 0) && (texture2Pixels[i] == 0xff000000))
            {
                return 1;
            }

        }
        return 0;
    }
    private void DrawHud()
    {
        string timeString = "TIME: " + timeElapsed.Minutes.ToString("00") + ":" + timeElapsed.Seconds.ToString("00");

        spriteBatch.Draw(hud, new Vector2(0, 0), Color.White);
        spriteBatch.DrawString(font, timeString, new Vector2(15, 35), Color.White);
        spriteBatch.DrawString(font, "Level " + (loadlevel + 1), new Vector2(15, 10), Color.White);
        spriteBatch.DrawString(font, "" + player.mvgelever, new Vector2(739, 55), Color.White);
        spriteBatch.DrawString(font, "" + player.problemelever, new Vector2(799, 55), Color.White);
        spriteBatch.DrawString(font, "" + player.normalaelever, new Vector2(859, 55), Color.White);
        spriteBatch.DrawString(font, "" + antwavers, new Vector2(454, 55), Color.White);
        spriteBatch.DrawString(font, "" + antpolis, new Vector2(514, 55), Color.White);
        spriteBatch.DrawString(font, "" + antvapen, new Vector2(574, 55), Color.White);
        spriteBatch.DrawString(font, "" + antlabb, new Vector2(633, 55), Color.White);
        spriteBatch.DrawString(font, "" + player.coins, new Vector2(359, 55), Color.White);
        spriteBatch.DrawString(font, "" + player.nyckel, new Vector2(328, 55), Color.White);
        spriteBatch.DrawString(font, "" + player.bombs, new Vector2(296, 55), Color.White);
        spriteBatch.DrawString(font, "RESEARCH IN: " + researchSpan, new Vector2(15, 55), Color.White);
    }
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin();
        switch (gameState)
        {
            case GameState.TITLESCREEN:
                break;
            case GameState.PLAYING:
                if (loadlevel == 0)
                    spriteBatch.Draw(level0, new Vector2(0, 0), Color.White);
                if (loadlevel == 1)
                    spriteBatch.Draw(level1, new Vector2(0, 0), Color.White);
                if (loadlevel == 2)
                    spriteBatch.Draw(level2, new Vector2(0, 0), Color.White);
                if (loadlevel == 3)
                    spriteBatch.Draw(level3, new Vector2(0, 0), Color.White);
                if (loadlevel == 4)
                    spriteBatch.Draw(level4, new Vector2(0, 0), Color.White);
                if (loadlevel == 5)
                    spriteBatch.Draw(level5, new Vector2(0, 0), Color.White);
                for (int i = 0; i < tile_position.Count; i++)
                {
                    switch (tile_life[i])
                    {
                        case 0: spriteBatch.Draw(tile_gfx, tile_position[i], Color.White);
                            break;
                        case 1: spriteBatch.Draw(tile_gfx, tile_position[i], Color.HotPink);
                            break;
                        case 2: spriteBatch.Draw(tile_gfx, tile_position[i], Color.YellowGreen);
                            break;
                        case 3: spriteBatch.Draw(tile_gfx, tile_position[i], Color.Purple);
                            break;
                        case 4: spriteBatch.Draw(tile_gfx, tile_position[i], Color.Yellow);
                            break;
                        case 5: spriteBatch.Draw(tile_gfx, tile_position[i], Color.Silver);
                            break;
                        case 6: spriteBatch.Draw(tile_gfx, tile_position[i], Color.Gold);
                            break;
                        case 60: spriteBatch.Draw(tile_gfx, tile_position[i], Color.Gold);
                            spriteBatch.Draw(player.PlayerTexture, new Vector2(tile_position[i].X + 40, tile_position[i].Y + 40), Color.White);
                            break;
                        case 7: spriteBatch.Draw(tile_gfx, tile_position[i], Color.Orange);
                            break;
                        case 70: spriteBatch.Draw(tile_gfx, tile_position[i], Color.DarkOrange);
                            break;
                        case 8: spriteBatch.Draw(stairsDown, tile_position[i], Color.White);
                            break;
                        case 9: spriteBatch.Draw(stairsUp, tile_position[i], Color.White);
                            break;
                    }
                }
                DrawHud();
                player.Draw(spriteBatch);
                break;
        }
        spriteBatch.End();
        base.Draw(gameTime);
    }
}
}

Player class (Player.cs):

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Innovationally
{
public class Player
{
    public Texture2D PlayerTexture;
    public Vector2 Position;
    public bool Active;
    public float angle;
    public int coins { get; set; }
    public int bombs { get; set; }
    public int normalaelever { get; set; }
    public int health { get; set; }
    public int damage { get; set; }
    public int maximumhealth { get; set; }
    public int problemelever { get; set; }
    public int mvgelever { get; set; }
    public int elever { get; set; }
    public int nyckel { get; set; }
    public int Width
    {
        get { return PlayerTexture.Width; }
    }
    public int Height
    {
        get { return PlayerTexture.Height; }
    }
    public void Initialize(Texture2D texture, Vector2 position)
    {
        PlayerTexture = texture;
        Position = position;
        Active = true;
        elever = 0;
        mvgelever = 0;
        normalaelever = 0;
        coins = 0;
        bombs = 0;
        health = 100;
        maximumhealth = 100;
        damage = 20;
        nyckel = 1;
        angle = (float)Math.PI * 2;
    }
    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(PlayerTexture, Position, null, Color.White, angle, new Vector2(PlayerTexture.Width / 2, PlayerTexture.Height / 2), 1f, SpriteEffects.None, 0f);
    }
}
}

And finally, Loot.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Innovationally
{
class Loot
{
    Player player = Game1.player;
    public Random myRnd = new Random();
    public int primarylootnumber;
    public int secondarylootnumber;
    public int tertiarylootnumber;
    public void RandomLoot()
    {
        primarylootnumber = myRnd.Next(1, 11);
        switch (primarylootnumber)
        {
            case 1:player.coins++;
                break;
            case 2:player.coins += 3;
                break;
            case 3:player.coins += 5;
                break;
            case 4:player.normalaelever += 1;
                break;
            case 5:player.normalaelever += 2;
                break;
            case 6:player.health += 10;
                break;
            case 7:player.health += 30;
                break;
            case 8:player.health += 50;
                break;
            case 9:player.damage += 5;
                break;
            case 10:player.damage += 10;
                break;
        }
        secondarylootnumber = myRnd.Next(1, 11);
        switch (primarylootnumber)
        {
            case 1:player.maximumhealth += 10;
                break;
            case 2:player.coins += 10;
                break;
            case 3:player.damage += 15;
                break;
            case 4:player.mvgelever += 1;
                break;
            case 5:player.problemelever += 1;
                break;
        }
        tertiarylootnumber = myRnd.Next(1, 31);
        switch (primarylootnumber)
        {
            case 10:
                player.mvgelever += 1;
                player.problemelever += 1;
                break;
            case 20:player.coins += 50;
                break;
            case 30:player.maximumhealth += 30;
                break;
        }

    }
    public void RandomResearch()
    {
        primarylootnumber = myRnd.Next(1, 6);
        switch (primarylootnumber)
        { 
            case 1:player.maximumhealth += 30;
                break;
            case 2: player.damage += 20;
                break;
            case 3:
                break;
            case 4:
                break;
            case 5:
                break;
        }
    }
}
}

The only red flag I can see at the moment is:

t1.GetData(0, r1, texture1Pixels, 0, pixelCount);
t2.GetData(0, r2, texture2Pixels, 0, pixelCount);

These calls have a high overhead. I would recommend, if you are doing per-pixel collision detection, caching or pre-computing the pixel data rather than retrieving the data from the texture every frame.

EDIT:

here is a toy implementation (NOT TESTED!) of how you may want to retrieve the pixel data. Hopefully this will be enough to get you started.

Here are two mini functions for getting the texture data. When initializing your game, use 'PixelMaps' passing in all of your textures (eg in an array). You will then have a dictionary you can use to perform lookups of the pixel data. You will want to use the information gleamed from the Texture and the rectangle you are testing to pick the right pixels.

    public static uint[] Pixels(Texture2D texture)
    {
        uint[] data = new uint[texture.Width * texture.Height];
        texture.GetData(data);
        return data;
    }
    public static Dictionary<Texture2D, uint[]> PixelMaps(IEnumerable<Texture2D> textures)
    {
        return textures.ToDictionary(t => t, Pixels);
    }

The TestCollision function may now look something like this (I have tried to keep the code as close as possible to the original).

    public static int TestCollision(uint[] t1,
        int t1Width,
        Rectangle r,
        uint[] t2)
    {
        for(var x = r.X; x < r.X + r.Width; x++)
            for (var y = r.Y; y < r.Y + r.Height; y++)
            {
                var i = x + (y * t1Width);
                if (((t1[i] & 0xff000000) > 0) && (t2[i] == 0xffC3C3C3))
                {
                    return 1;
                }
                if (((t1[i] & 0xff000000) > 0) && (t2[i] == 0xff000000))
                {
                    return 2;
                }
                if (((t1[i] & 0xff000000) > 0) && (t2[i] == 0xff000000))
                {
                    return 1;
                }
            }
        return 0;
    }

Hopefully that is enough to get you started! Have fun :)

EDIT2:

Ok more information, again, trying to not change your code too much here we go:

introduce a new private member for the pixel maps. eg:

Dictionary<Texture2D, uint[]> pixelMaps;

In your init function try this (btw I would also put the levels in a dictionary using an enum as a key, look it up. You should never have fields/properties like 0,1,2,3 that just screams I need some kind of list/array/assoc data structure.):

       //LEVEL STATS
  level0 = Content.Load<Texture2D>("level0");
  level1 = Content.Load<Texture2D>("level1");
  level2 = Content.Load<Texture2D>("level2");
  level3 = Content.Load<Texture2D>("level3");
  level4 = Content.Load<Texture2D>("level4");
  level5 = Content.Load<Texture2D>("level5");
  //As you can see I'm using the new PixelMaps function here... 
  pixelMaps = PixelMaps(new[] { level0, level1, level2, level3, level4, level5 });

When you come to test collisions you can do this:

var playerMap = pixelMaps[player.PlayerTexture];
var level0Map = pixelMaps[level0];
if (loadlevel == 0)
     bHit = TestCollision(playerMap, r1, level0Map);

The best and quickest way to solve this problem (and many others while your at it) is to use a profiler. It will be able to show you what methods (and possibly lines, ANTS supports this) are taking the longest to complete. Once you narrow it down you can figure out how to fix these individual problems.

As for the code you posted, I'd have to agree with thedajaw that the collision code has alot of room for optimization. Getting the texture data everyframe is never a good idea, and should be done once at the begining of the game and stored in a variable for later use.

Overall I think the rest is fine, but see what the profiler shows up. Some minor structure issues (Could use some else ifs instead of ifs, things could be handled better,etc)

I'd recommend to read this question I asked a while back on a simlar subject.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM