Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm kind of new to XNA (and games in general). I'm trying to make a young relative of mine (10yo) a simple fun game where he is the main character. I'm down to the wire (I have to get it to him by tomorrow and wont have time to work on it at all tomorrow, which means I have to finish it tonight). Everything was tight as it is (I'm kind of close to being finished, but not really). Everything was going pretty smoothly until a minute ago when I asked monsters and finished the level map data.

Here is a copy of what the map data looks like:

enter image description here

Its only 16x100. Basically, each color represents an object already specified in other parts of the game. I'm doing this for simplicity. Parsing the image itself happens nearly instantaneous so don't yell at me for doing it this way. The maps are pretty straight forward and don't need any real optimization.

enter image description here

Now, my first guess was, having this many monsters causes maaaaaaajor lag... (notice the 2 FPS).

- that's only half of the story -

Apparently, when I added the details for the upper half of the map image (It originally only had a couple of lines on it for testing) it causes a ton of the lag being involved.


So why does it cause so much lag?

Well, this is the embarrassing part. Because the player will be working on, there will be a lot of jumping involved. Because of this, I have to pan the 'camera' while he moves vertically. So, instead of the character moving, everything else actually moves. (All of the trusses, monsters, etc. move instead of the character). So basically, the character is locked vertically (he can move freely horizontally, however). The monsters cause a bit of lag but really, not anything noticeable unless both issues are combined.

Basically, the level is loaded into a multidimensional array. Then, whenever any vertical change occurs to the player (gravity or jumping), an offset equivalent to the amount I want it to appear that he is jumping is applied to EVERYTHING (this includes monsters and every block).

The game is made out of 64x64 sprites so there are a total of 10 ladder sprites visible in the screenshot above being moved every time the player moves.


Why I feel dumb...

First, I recognize the problem: To many sprites are being moved at the same time. Second, I recognize the solution: Only move sprites that are visible or vital (such as monsters). The problem is, I'm not sure how to only move the sprites that are visible. I tried dividing everything into vertical chunks of 10 and then check to see which ones are visible and only calculate those but things started getting out of hand! I don't want to ruin the game in the last hours I have working on it!

Oh, and did I mention? I've wired Lua in, I'm not even compiling the actual code! I'm scripting almost EVERYTHING!

So, it is my plea to you this day (the very last day of the year no less)! To help me find a quick (dirty if necessary!) solution to my problem!

If it helps, here is a copy of the level.lua file I'm currently dealing with:

module(...,package.seeall)

local spriteTable={
{name="Blue Truss",color={r=0,g=148,b=255},position={x=0,y=0}};
{name="Red Truss",color={r=255,g=0,b=55},position={x=1,y=0}};
{name="Ladder",color={r=255,g=106,b=0},position={x=0,y=1}};
{name="Stone",color={r=0,g=0,b=0},position={x=1,y=1}};
{name="MonsterBox",color={r=188,g=75,b=0},position={x=0,y=3}};
}

currentLevel={map=nil,offset=0,toffset=0,cerrain={},spawn={x=14,y=96},character=nil}

local LEVELMAX=10

function getVisibleChunks()
    local visible={}
    Print(#currentLevel.map)
    for _,v in ipairs(currentLevel.map) do
        --CHECK TOP
        local pos=base.HEIGHT-((#currentLevel.map-_*LEVELMAX)+1)*64-currentLevel.toffset
        if pos>0 and pos<base.HEIGHT then
            Print("CHUNK ".._.." IS VISIBLE!")
        end
        --CHECK BOTTOM
        pos=base.HEIGHT-((#currentLevel.map-_*LEVELMAX)+1)*64-currentLevel.toffset+(LEVELMAX*64)
        if pos>0 and pos<base.HEIGHT then
            Print("CHUNK ".._.." IS VISIBLE!")
        end
    end
end

function play(level,callback)
    currentLevel.map=loadLevel(level)
    --getVisibleChunks()
    me:AddSprite("levelback",0,0,base.WIDTH,base.HEIGHT,0,0,base.WIDTH,base.HEIGHT,1)
    for _y=#currentLevel.map,1,-1 do
        local top=currentLevel.map[_y]
        for _x,obj in pairs(top) do
            table.insert(currentLevel.cerrain,me:AddSprite("objects",(_x-1)*64,base.HEIGHT-(#currentLevel.map-_y+1)*64-currentLevel.toffset,64,64,obj.position.x*64,obj.position.y*64,64,64,2))
        end
    end
    collision.loadTops(currentLevel.map)
    ConnectEvent(base,"keydown",function(input)
        character.sendInput({["direction"]=true,["key"]=input})
    end)
    ConnectEvent(base,"keyup",function(input)
        character.sendInput({["direction"]=false,["key"]=input})
    end)
    ConnectEvent(base,"updated",function(t)
        currentLevel.toffset=currentLevel.toffset-currentLevel.offset
        for _,v in ipairs(currentLevel.cerrain) do
            v:SetPosition(v.position.X,v.position.Y+currentLevel.offset)
        end
        currentLevel.offset=0
        character.update(t)
        monsters.update(t)
        debugger.set("OFFSET: "..currentLevel.toffset)
    end)
    currentLevel.character=me:AddSprite("character",currentLevel.spawn.x*64,base.HEIGHT-(100-currentLevel.spawn.y)*64+29,64,99,128,0,64,99,5)
    currentLevel.toffset=0
    currentLevel.offset=0
    character.set(currentLevel.character,currentLevel.character.position.x,currentLevel.character.position.y)
end

function loadLevel(level)
    local imgmap=me:GetBitmapFromFile("maps\\"..level..".png")
    local map={}
    local cCount=0
    for y=0,imgmap.Height-1 do
        if cCount==0 then
            table.insert(map,{})
        end
        cCount=cCount+1
        local xLevel={}
        for x=0,imgmap.Width-1 do
            local fColor=imgmap:GetPixel(x,y)
            local r=fColor.R
            local g=fColor.G
            local b=fColor.B
            for _,v in ipairs(spriteTable) do
                if (r==v.color.r) and (g==v.color.g) and (b==v.color.b) then
                    xLevel[x+1]={name=v.name,position=v.position}
                end
            end
        end
        --if cCount>=LEVELMAX then
        --  cCount=0
        --end
        --map[#map][y+1]=xLevel
        map[y+1]=xLevel
    end
    return map
end

Here are my edits:

So, the camera pan thing worked nearly perfectly! With some final tuneups, I'm not having any FPS drops at all!

Now, however, (as dumb as this sounds) I've got this issue here:

enter image description here

Is there a way to force some of these elements (such as the background image and the debug info) to stay instead of panning along with everything else?

share|improve this question

1 Answer 1

up vote 7 down vote accepted

This will be hard to fix in so little time, especially since you're using such a peculiar structure, but well, let's give it a try. I'll post and edit as I notice the most glaring problems.

Step 1) Proper Camera Panning

The first thing that jumped out of your post like a madman was the part where you said:

So, instead of the character moving, everything else actually moves.

This is a complete NO! You don't need to move anything in the world to simulate a camera. The only thing you need to do is to pass a view matrix containing the camera translation to the SpriteBatch. This is literally, one line of code:

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, Matrix.CreateTranslation(-cameraPosition));

Just this! Then all you have to do is change the cameraPosition value and everything will be panned for you at no performance cost. (By the way, notice the minus sign). Or better yet, since the camera will always be centered on the player:

  1. Unlock that player, lock the scenario! Let the player be the one that moves around (and in both axes).
  2. Instead of using some cameraPosition value, just use the playerPosition directly, minus half of the screen size just to make it centered (and don't forget to negate at the end too).

Step 2) Visibility Check

You don't mention anything about this (unless I missed it) but are you perhaps drawing all the tiles at once? If you're doing that, then it's obviously a major source of problems too. Your camera can only see a certain amount of tiles at once, so you only need to draw those tiles.

Step 3) "Too many monsters"

Actually, I doubt it. I've managed to draw thousands and thousands of sprites at once, and never did the application grind to 2 FPS. The amount of monsters you have does not seem like much of a problem. But just to be sure and to gain a little bit of performance, make sure you don't draw monsters that are outside of view.

Step 4) Disable panning on some elements

Is there a way to force some of these elements (such as the background image and the debug info) to stay instead of panning along with everything else?

Simple! Just draw those elements in a separate SpriteBatch block without passing the matrix. For instance:

spriteBatch.Begin();
// Draw background
spriteBatch.End();

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, Matrix.CreateTranslation(-cameraPosition));
// Draw world
spriteBatch.End();

spriteBatch.Begin();
// Draw debug info
spriteBatch.End();
share|improve this answer
    
So, if I have something drawn at a negative position, will panning the camera to it show it? –  Xan Jan 1 '12 at 3:58
    
My main issue with monsters only really showed issues when it was happening at the same time as also drawing the larger map, otherwise it was fine. It took some resources to 'initialize' the usage of the monsters to begin with which probably was the issue, not the number of monsters necessarily. –  Xan Jan 1 '12 at 4:08
    
I am implementing the camera method now. I'll be back in a couple of minutes to follow up. Thanks with all of the help so far! –  Xan Jan 1 '12 at 4:08
1  
Yes it will show everything. You can call it a camera, but really it's just a translation, like you were doing manually. But the big difference is that it's done automatically for you on the GPU with a single multiplication, so it's virtually instantaneous. –  David Gouveia Jan 1 '12 at 4:20
1  
:) Glad it got sorted out this easily. I expected a lot worse. –  David Gouveia Jan 1 '12 at 4:24

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.