Why do games sometimes skip frames in monogame/xna? - c#

This is a simple project created from MonoGame Cross-Platform Desktop template. I created a ball Texture2D and tried to make it move.So I added some code to the Update function.
position.X += 3;
And then in the Draw function
_spriteBatch.Begin();
_spriteBatch.Draw(ballTexture, position, Color.White);
_spriteBatch.End();
I can see the ball flashing sometimes.
Why is that and what can I do to make it not skip frames?
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace SimpleTest
{
public class Game1 : Game
{
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
private Texture2D dirtTexture;
private Vector2 position;
private int direction = 1;
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
dirtTexture = Content.Load<Texture2D>("dirt");
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
// TODO: Add your update logic here
base.Update(gameTime);
if (position.X > 600)
direction = -1;
else if (position.X < 100)
direction = 1;
position.X = position.X + direction * 3;
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
_spriteBatch.Begin();
_spriteBatch.Draw(dirtTexture, position, Color.White);
_spriteBatch.End();
base.Draw(gameTime);
}
}
}

I think base.Update(gameTime); should only be placed at the very end of the Update() Method, Just like base.Draw(gameTime);. That way it won't skip any lines.

I guess your PC might be running slowly which is the problem, and not your code. To test it, you can replace your Draw method as follows:
protected override void Draw(GameTime gameTime)
{
Color color=gameTime.IsRunningSlowly?Color.Red:Color.CornflowerBlue;
GraphicsDevice.Clear(color);
// TODO: Add your drawing code here
_spriteBatch.Begin();
_spriteBatch.Draw(dirtTexture, position, Color.White);
_spriteBatch.End();
base.Draw(gameTime);
}
Everything is the same, except now we are colouring the screen as Red instead of the usual blue only if the game is in fact running slow (Which is due to your PC). We can know this by the gameTime variable as shown in the first two lines of the method. Test it out and see if you can see the game background going red when the "frame-skipping" happens.
I did test your exact code in my PC and it was working fine. Needless to say, I did use some other png I had instead of the "dirt" Texture2D you used but I don't think that it should be necessary.

Perhaps it's due to GC. You could use my -almost- garbage free build to verify.
You can install the updated SDK from here or the nuget package.

Related

Monogame - Changing background colour error

I'm building a 2D platformer and I want to have different colour backgrounds for each level. I've made an object that when collided with, it places the character onto the next level by changing the player.Position, like so...
protected override void Update(GameTime gameTime){
if (player.Bounds.Intersects(teleportObj.Bounds))
{
GraphicsDevice.Clear(Color.SlateGray); // fails to change bg color
player.Position = new Vector2(172, 0); // successfully changes character position
MediaPlayer.Play(dungeonSong); // successfully plays new song
MediaPlayer.IsRepeating = true; // successfully repeats new song
}
}
I have already set a background for the first level to begin with in the Game1's Draw() function like this:
GraphicsDevice.Clear(Color.CornflowerBlue);
But when my player collides with teleportObj, the background color doesn't change.
GraphicsDevice.Clear(Color.SlateGray); is used in Draw function. Try creating new Color variable, and change that variable in Update method, and when using GraphicsDevice.Clear(name of the variable);, use it in Draw function.
Code for that would look like this:
Color backgroundColor = Color.CornflowerBlue;
protected override void Update(GameTime gameTime)
{
if (player.Bounds.Intersects(teleportObj.Bounds))
{
backgroundColor = Color.SlateGray;
player.Position = new Vector2(172, 0);
MediaPlayer.Play(dungeonSong);
MediaPlayer.IsRepeating = true;
}
else backgroundColor = Color.CornflowerBlue;
}
protected override void Draw(SpriteBatch spriteBatch)
{
GraphicsDevice.Clear(backgroundColor);
*draw other stuff*
}

Moving Draw() code to a Class

I'm creating a game in monogame, and I've loaded tiles in my game inside the Draw() function like so:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(danChar, charPosition, Color.White);
// loop below loads the 'grass' tiles only
// assuming gameworld size of 770x450
for (int i = 0; i < 770; i += 31) // adds 31 to i (per tile)
{
position = new Vector2(i, 392); // places incrementation into vector position
spriteBatch.Draw(gameTile, position, Color.White); // draws the tile each time
if (i == 744)
{
i = i + 26; // fills last space between 744 and 770
position = new Vector2(i, 392);
}
spriteBatch.Draw(gameTile, position, Color.White);
}
// loop below loads the brick tiles only (ones without grass)
spriteBatch.End(); // ends the spriteBatch call
base.Draw(gameTime);
}
However I would prefer that this was a separate class rather than being placed directly into the draw function, however I'm not too sure how to do this and would appreciate any help given.
Thanks in advance!
If you just want to move the code as is to another class, create your class (e.g. something like GameWorld seems to appropriate for your code)
public class GameWorld
{
// You may wish to move your gameTile definition into this class if it is the only
// class that uses it, and handle the content loading for it in here.
// e.g. if you're currently loading the texture in the LoadContent method in your game
// class, create a LoadContent method here and pass in ContentManger as a parameter.
// I've passed in the texture as a parameter to the Draw method in this example to
// simplify as I'm not sure how you're managing your textures.
public void Draw(SpriteBatch spriteBatch, GameTime gameTime, Texture2D gameTile)
{
// loop below loads the 'grass' tiles only
// assuming gameworld size of 770x450
for (int i = 0; i < 770; i += 31) // adds 31 to i (per tile)
{
Vector2 position = new Vector2(i, 392); // places incrementation into vector position
spriteBatch.Draw(gameTile, position, Color.White); // draws the tile each time
if (i == 744)
{
i = i + 26; // fills last space between 744 and 770
position = new Vector2(i, 392);
}
spriteBatch.Draw(gameTile, position, Color.White);
}
// loop below loads the brick tiles only (ones without grass)
}
}
Then the Draw method in your Game class would look like
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(danChar, charPosition, Color.White);
// Assuming you've created/loaded an instance of the GameWorld class
// called gameWorld in Initialize/LoadContent
gameWorld.Draw(spriteBatch, gameTime, gameTile);
spriteBatch.End(); // ends the spriteBatch call
base.Draw(gameTime);
}
Just make sure you're calling the Draw methods in the correct order. e.g. you want your player to appear above any background tiles.
I believe the default SpriteSortMode is Deferred which draws in the order the calls are made (i.e. from the back to the front).
You can specify a different SpriteSortMode in your call to spriteBatch.Begin() if you need to but for a simple game just move the Draw calls around.
More info on SpriteSortMode at MSDN if needed.
Similarly you can chain your Update, LoadContent methods into these classes if you wish, making sure to pass in anything you need as arguments.
Update:
To define gameWorld as an instance of the GameWorld class, you define it near the top of your game class, then typically initialize it in the Initialize method.
So your game class will look something like
public class MyGameName : Microsoft.Xna.Framework.Game
{
private SpriteBatch spriteBatch;
// other variable declarations
// Add a declaration for gameWorld
private GameWorld gameWorld;
protected override Initialize()
{
// Add the following line to initialize your gameWorld instance
gameWorld = new GameWorld();
}
// other existing code - your LoadContent, Update, Draw methods etc.
}

Redraw only one texture, not all of them

I'm a little noob with XNA and I've encountered a problem. I have one texture called "dot", that I use to draw a line across the screen. Then I have a texture that I call "tool", that I want to move around when I touch it with my finger. The problem I have is that for the tool to be cleared and redrawn I call the graphicsDevice.Clear-method in XNA. But when I do that, my lines also disappears. So my question is how can i move around the tool, without causing my lines to disappear? Is there a way to only redraw the tool, without redrawing the lines? Heres a snippet of my code, first update-method:
protected override void Update (GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
TouchCollection touchCollection = TouchPanel.GetState ();
foreach (TouchLocation tl in touchCollection) {
if ((tl.State == TouchLocationState.Pressed) || (tl.State == TouchLocationState.Moved)) {
toolPos = touchCollection [0].Position;
}
}
base.Update (gameTime);
}
And the draw-method:
protected override void Draw (GameTime gameTime)
{
graphics.GraphicsDevice.Clear (Color.Black);
newVector = nextVector (oldVector);
spriteBatch.Begin ();
DrawEvenlySpacedSprites (dot, oldVector, newVector, 0.9f);
spriteBatch.Draw (tool, toolPos, Color.White);
spriteBatch.End ();
oldVector = new Vector2 (newVector.X, newVector.Y);
base.Draw (gameTime);
}
Try using a RenderTarget2D with RenderTargetUsage.PreserveContents. If you didn't use render targets yet, here's a tutorial: http://rbwhitaker.wikidot.com/render-to-texture
All you have to do is to specify RenderTargetUsage.PreserveContents in target's constructor, draw dots to it, and draw the target and tool to the screen. It should look like this:
protected override void Draw (GameTime gameTime)
{
graphics.GraphicsDevice.Clear (Color.Black);
newVector = nextVector (oldVector);
GraphicsDevice.SetRenderTarget(renderTarget);
spriteBatch.Begin ();
DrawEvenlySpacedSprites (dot, oldVector, newVector, 0.9f);
spriteBatch.End ();
GraphicsDevice.SetRenderTarget(null);
spriteBatch.Begin();
spriteBatch.Draw(renderTarget, new Vector2(), Color.White);
spriteBatch.Draw (tool, toolPos, Color.White);
spriteBatch.End()
oldVector = new Vector2 (newVector.X, newVector.Y);
base.Draw (gameTime);
}

C# XNA 3D Model not rendering

The model is a .fbx
I have tried many models also, Nothing seems to be working i was wondering if anyone could help me, The following code is all I have thanks!
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;
namespace _3D
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Model model;
Matrix[] transforms;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferHeight = 800;
graphics.PreferredBackBufferWidth = 1280;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
model = Content.Load<Model>("3d/screwdriver");
transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
// The draw method is one of the reasons I think nothing is working,
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
Matrix view = Matrix.CreateLookAt(
new Vector3(200, 300, 900),
new Vector3(0, 50, 0),
Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10000.0f);
Matrix baseWorld = Matrix.CreateScale(0.4f) * Matrix.CreateRotationY(MathHelper.ToRadians(180));
foreach (ModelMesh mesh in model.Meshes)
{
Matrix localWorld = transforms[mesh.ParentBone.Index] * baseWorld;
foreach (ModelMeshPart part in mesh.MeshParts)
{
BasicEffect e = (BasicEffect)part.Effect;
e.World = localWorld;
e.View = view;
e.Projection = projection;
e.EnableDefaultLighting();
}
mesh.Draw();
}
base.Draw(gameTime);
}
}
}
I don't see anything really wrong with your code, so the problem may be on the model.
It might be caused by the model file in question. There is a know issue with the stock FBX Exporter on Blender (I don't know which modelling software you used to make the model, but the matter is still relevant) that makes the model be exported with a size 100x bigger than it is on Blender. The result is that the model is so big that it not only encloses the camera inside itself, but also is so big that it's polygons are beyond the viewing frustrum. Result: the model is indeed rendered, but it'll never appear onscreen unless you move the camera around until you realise the scaling problem.
I'm saying this because I dealed with this problem myself. If you are indeed using Blender, you should use the exporter found in this page:
http://blog.diabolicalgame.co.uk/2011/07/exporting-animated-models-from-blender.html

Timer drawing on top of itself, Not clearing

Interesting problem! Basically I have a timer for my UI that counts down from 99. Everything works fine save for the fact its drawing the new time on top of the old time. (The previous time is not clearing) There is a clear happening within the GameplayScreens draw call though... its just weird.
Here are my related functions:
GameplayScreen.cs (or our usual Game1)
public override void Update(GameTime gameTime)
{
m_GameUI.Update(gameTime);
m_GameCam.lookAt(m_Hero.Position);//feeds the camera's lookAt function the players vector2 position
m_GameUI.lookAt(m_GameCam.Position); //Look at the camera's position :D
}
public override void Draw(GameTime gameTime)
{
ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
Color.CornflowerBlue, 0, 0);
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
m_BackgroundManager.Draw(spriteBatch, m_GameCam);
//Begin the Spritebatch Drawing
spriteBatch.Begin(SpriteSortMode.Immediate, null,
null,
null,
null,
null, m_GameCam.ViewMatrix);
//Call EntityManager.DrawAllEntities()
EntityManager.Instance.DrawAllEntities(gameTime, spriteBatch);
//End the spritebatch drawing
spriteBatch.End();
spriteBatch.Begin();
m_GameUI.Draw(gameTime, spriteBatch);
m_PlayerUI.Draw(gameTime, spriteBatch);
spriteBatch.End();
}
GameUI.cs
SpriteFont m_Font;
double m_Timer;
public virtual void Update(GameTime gameTime)
{
m_Timer -= gameTime.ElapsedGameTime.TotalSeconds;
}
public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
spriteBatch.Draw(UITexture, Position, Color.White);
spriteBatch.DrawString(m_Font, "" + (int)m_Timer + "", new Vector2(380, 45), Color.White);
}
Ended up being a really silly mistake, I had a class deriving from the UI that was calling base.draw() so both the updated clock and the original value were being displayed on the screen at once. Basically poor code design, everyone is a newbie when they start out right?
Thanks for looking into it :)

Categories

Resources