 |
BOOK: Professional XNA Game Programming: For Xbox 360 and Windows ISBN: 978-0-470-12677-6  | This is the forum to discuss the Wrox book Professional XNA Game Programming: For Xbox 360 and Windows by Benjamin Nitschke; ISBN: 9780470126776 |
|
Welcome to the p2p.wrox.com Forums.
You are currently viewing the BOOK: Professional XNA Game Programming: For Xbox 360 and Windows ISBN: 978-0-470-12677-6 section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

January 14th, 2008, 10:39 AM
|
|
Registered User
|
|
Join Date: Jan 2008
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Ball movement problems
This will probably end up being a rather silly issue. Been working my way through the pong example in chapter 2. I've just finished the bit on page 47, where the ball should move but no collision deetection has been added. However, the ball only moves very jerkily in the center of the screen. Just sort of pops around inside of about a half-inch by half-inch area. Here's the math bit in my update method that should move the ball:
ballPosition += ballSpeedVector *
moveFactorPerSecond * BallSpeedMultiplicator;
That's a straight copy from the book and none of the erratta seems to touch on this. Has anyone else had any problmes? I've also reviewed the example code for download. The base movement line is the same as above. I know there are also a bunch of collision detection checks in the example too, but I'm just not to that point and wondering if the behaviour I'm seeing is expected or not. Seems as though I should see more fluid movement, just not collision, at this point.
Thanks in advance for any help. I can also post the whole code file if need be. Just didn't wanna overrun everyone with code.
|
|

January 15th, 2008, 02:50 PM
|
|
Registered User
|
|
Join Date: Jan 2008
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
I thought since no one had replied just yet I would go ahead and post the code in it's entirety. I'm hoping it just comes down to a silly mistake on my part. Not getting any compile errors, just a couple of values set but not used warnings, but I know why those are there (because not reached that part of the book yet).
Code:
using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace pong
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class PongGame : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public PongGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
public static void StartGame()
{
using (PongGame game = new PongGame())
{
game.Run();
}//using
}//StartGame()
static TestPongGame testGame;
static void StartTest(TestDelegate testLoop)
{
using (testGame = new TestPongGame(testLoop))
{
testGame.Run();
} //using
} //StartTest(testLoop)
// Global Vars
Texture2D backgroundTexture, menuTexture, gameTexture;
int width = 0;
int height = 0;
int leftPlayerLives = 0;
int rightPlayerLives = 0;
///<summary>
/// Current Paddle Postions, 0 means top, 1 means bottom
///</summary>
float leftPaddlePosition = 0.5f,
rightPaddlePostion = 0.5f;
///<summary>
///Current ball psotion, again from 0 to 1, 0 is left and top,
/// 1 is bottom and right
///</summary>
Vector2 ballPosition = new Vector2(0.5f, 0.5f);
///<summary>
///Ball speed vector, randomized for every new ball.
/// Will be set to Zero if we are in menu or game is over.
///</summary>
Vector2 ballSpeedVector = new Vector2(0, 0);
///<summary>
/// Ball speed multiplicator, this is how much scree space the ball
/// will travel each second.
///</summary>
const float BallSpeedMultiplicator = 0.5f;
///<summary>
/// Computer paddle speed. If the ball moves faster up or down than
/// this, the computer paddle can't keep up and finally we will win.
///</summary>
const float ComputerPaddleSpeed = 0.5f;
///<summary>
/// Game Modes
///</summary>
enum GameMode
{
Menu,
Game,
GameOver,
}
///<summary>
/// Are we in multiplayer game? If this is false, the computer
/// controls the left paddle.
///</summary>
bool multiplayer = false;
///<summary>
/// Game mode we are currently in
///</summary>
GameMode gameMode = GameMode.Menu;
///<summary>
/// Currently Selected Menu Item
///</summary>
int currentMenuItem = 0;
//Rectangles
static readonly Rectangle
XnaPongLogoRect = new Rectangle(0, 0, 512, 110),
MenuSingleplayerRect = new Rectangle(0,110,512,38),
MenuMultiplayerRect = new Rectangle(0,148,512,38),
MenuExitRect = new Rectangle(0,185,512,38),
GameLivesRect = new Rectangle(0,222,155,35),
GameRedWonRect = new Rectangle(151,222,155,34),
GameBlueWonRect = new Rectangle(338,222,165,34),
GameRedPaddleRect = new Rectangle(23,0,22,92),
GameBluePaddleRect = new Rectangle(0,0,22,92),
GameBallRect = new Rectangle(1,94,33,33),
GameSmallBallRect = new Rectangle(37,108,19,19);
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
// Remember resolution
width = graphics.GraphicsDevice.Viewport.Width;
height = graphics.GraphicsDevice.Viewport.Height;
//Set Lives
leftPlayerLives = 3;
rightPlayerLives = 3;
//load graphics
backgroundTexture = Content.Load<Texture2D>("SpaceBackground");
menuTexture = Content.Load<Texture2D>("PongMenu");
gameTexture = Content.Load<Texture2D>("PongGame");
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
public void RenderSprite(Texture2D texture, int x, int y, Rectangle sourceRect, Color color)
{
sprites.Add(new SpriteToRender(texture, new Rectangle(x, y, sourceRect.Width, sourceRect.Height), sourceRect, color));
}
public void RenderSprite(Texture2D texture, Rectangle rect, Rectangle? sourceRect, Color color)
{
sprites.Add(new SpriteToRender(texture, rect, sourceRect, color));
}//RenderSprite(texture, rect, sourceRect, color)
public void RenderSprite(Texture2D texture, int x, int y, Rectangle sourceRect)
{
sprites.Add(new SpriteToRender(texture, new Rectangle(x,y,sourceRect.Width,sourceRect.Height),sourceRect,Color.LightGray));
}//RenderSprite(texture, rect, sourceRect)
public static void TestGameSprites()
{
StartTest(
delegate
{
//Show Lives
testGame.ShowLives();
//Ball in Center
testGame.RenderBall();
//Render both paddles
testGame.RenderPaddles();
//Start ball
testGame.StartNewBall();
}
);
}//TestGameSprites()
/// <summary>
/// Start new ball at the beginning of each game and when a ball is lost.
/// </summary>
public void StartNewBall()
{
ballPosition = new Vector2(0.5f, 0.5f);
Random rnd = new Random((int)DateTime.Now.Ticks);
int direction = rnd.Next(4);
ballSpeedVector =
direction == 0 ? new Vector2(1, 0.8f) :
direction == 1 ? new Vector2(1, -0.8f) :
direction == 2 ? new Vector2(-1, 0.8f) :
new Vector2(-1, -0.8f);
}//StartNewBall();
public void ShowLives()
{
//Left Player Lives
RenderSprite(menuTexture, 2, 2, GameLivesRect);
for (int num = 0; num < leftPlayerLives; num++)
{
RenderSprite(gameTexture, 2 + GameLivesRect.Width +
GameSmallBallRect.Width * num - 2, 9, GameSmallBallRect);
}
//Right Player Lives
int rightX = 1024 - GameLivesRect.Width - GameSmallBallRect.Width * 3 - 4;
RenderSprite(menuTexture, rightX, 2, GameLivesRect);
for (int num = 0; num < rightPlayerLives; num++)
{
RenderSprite(gameTexture, rightX + GameLivesRect.Width +
GameSmallBallRect.Width * num - 2, 9, GameSmallBallRect);
}
}//ShowLives()
public void RenderBall()
{
RenderSprite(gameTexture,
(int)((0.05f + 0.9f * ballPosition.X) * 1024) -
GameBallRect.Width / 2,
(int)((0.02f + 0.96f * ballPosition.Y) * 768) -
GameBallRect.Height / 2,
GameBallRect);
}//RenderBall()
public void RenderPaddles()
{
RenderSprite(gameTexture,
(int)(0.05f * 1024) - GameRedPaddleRect.Width / 2,
(int)((0.06f + 0.88f * leftPaddlePosition) * 768) -
GameRedPaddleRect.Height / 2,
GameRedPaddleRect);
RenderSprite(gameTexture,
(int)(0.95f * 1024) - GameBluePaddleRect.Width / 2,
(int)((0.06f + 0.88f * rightPaddlePostion) * 768) -
GameBluePaddleRect.Height / 2,
GameBluePaddleRect);
}//RenderPaddles()
public static void TestMenuSprites()
{
StartTest(
delegate
{
testGame.RenderSprite(testGame.menuTexture,
512 - XnaPongLogoRect.Width / 2, 150,
XnaPongLogoRect);
testGame.RenderSprite(testGame.menuTexture,
512 - MenuSingleplayerRect.Width / 2, 300,
MenuSingleplayerRect);
testGame.RenderSprite(testGame.menuTexture,
512 - MenuMultiplayerRect.Width / 2, 350,
MenuMultiplayerRect,Color.Orange);
testGame.RenderSprite(testGame.menuTexture,
512 - MenuExitRect.Width / 2, 400,
MenuExitRect);
});
}//test menu sprites
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
//Get current keyboard states
KeyboardState keyboard = Keyboard.GetState();
//move half way across the screen each second
float moveFactorPerSecond = 0.5f *
(float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
//ESCAPE to quit
if (keyboard.IsKeyDown(Keys.Escape))
{
this.Exit();
}
//MOve up if key is pressed
if (keyboard.IsKeyDown(Keys.Up))
{
rightPaddlePostion -= moveFactorPerSecond;
}
//Move down if key is pressed
if (keyboard.IsKeyDown(Keys.Down))
{
rightPaddlePostion += moveFactorPerSecond;
}
//Enable Multiplayer keyboard inputs
if (multiplayer)
{
if (keyboard.IsKeyDown(Keys.W))
{
leftPaddlePosition -= moveFactorPerSecond;
}
if (keyboard.IsKeyDown(Keys.S))
{
leftPaddlePosition += moveFactorPerSecond;
}
}//if
else
{
//Just let the computer follow the ball position
float computerChange = ComputerPaddleSpeed * moveFactorPerSecond;
if (leftPaddlePosition > ballPosition.Y + computerChange)
leftPaddlePosition -= computerChange;
if (leftPaddlePosition < ballPosition.Y - computerChange)
leftPaddlePosition += computerChange;
}//else
//Make sure the paddles stay on screen
if (leftPaddlePosition < 0)
leftPaddlePosition = 0;
if (leftPaddlePosition > 1)
leftPaddlePosition = 1;
if (rightPaddlePostion < 0)
rightPaddlePostion = 0;
if (rightPaddlePostion > 1)
rightPaddlePostion = 1;
//update ball position
ballPosition += ballSpeedVector *
moveFactorPerSecond * BallSpeedMultiplicator;
//end paddle area
base.Update(gameTime);
}
public void DrawSprites()
{
//Don't render if there are no sprites this frame
if (sprites.Count == 0)
{
return;
}
//Start Rendering Sprites
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.None);
//Render all sprites
foreach (SpriteToRender sprite in sprites)
{
spriteBatch.Draw(sprite.texture,
//Rescale to fit resolution
new Rectangle(
sprite.rect.X * width / 1024,
sprite.rect.Y * height / 768,
sprite.rect.Width * width / 1024,
sprite.rect.Height * height / 768),
sprite.sourceRect, sprite.color);
//We are done, draw everything on screen with help of the end method.
}
spriteBatch.End();
//kill list of remembered sprites
sprites.Clear();
}//DrawSprites()
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
//DrawSprites
spriteBatch.Begin();
spriteBatch.Draw(backgroundTexture, new Rectangle(0, 0, width, height), Color.LightGray);
spriteBatch.End();
DrawSprites();
base.Draw(gameTime);
}
List<SpriteToRender> sprites = new List<SpriteToRender>();
}
delegate void TestDelegate();
class TestPongGame : PongGame
{
TestDelegate testLoop;
public TestPongGame(TestDelegate setTestLoop)
{
testLoop = setTestLoop;
}//TestPongGame(setTestLoop)
protected override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
testLoop();
}
}//class TestPongGame
class SpriteToRender
{
public Texture2D texture;
public Rectangle rect;
public Rectangle? sourceRect;
public Color color;
public SpriteToRender(Texture2D setTexture, Rectangle setRect,
Rectangle? setSourceRect, Color setColor)
{
texture = setTexture;
rect = setRect;
sourceRect = setSourceRect;
color = setColor;
}//SpriteToRender(setTexture, setRect, setColor)
} //class SpriteToRender
}
|
|

January 18th, 2008, 05:25 AM
|
|
Wrox Author
|
|
Join Date: May 2007
Posts: 29
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Hi lyndonh,
Not exactly sure what the issue is, the only thing I can think of is that either the value of ballSpeedVector is way too high or the moveFactorPerSecond is not a small enough value. The reason for this could be either a very low frame rate (you should check that, how many frames does the game have? maybe use fraps ( http://fraps.softonic.de/) to check it). Less than 10 or 20 frames per second would be bad, usually you have 60 or more frame regardless of using a fixed time-step (default in XNA) or not (default in most of my games).
I would first of all set the moveFactorPerSecond variable to a fixed value (like 0.01f) and see if this changes anything, maybe the calculation for this variable is not correct for your game settings. If you use a fixed time step (set Game.IsFixedTime to true) you can safely use a fixed value for moveFactorPerSecond because the method will only be called every 1/60 of a second (results to a value of 0.0166f).
If you still have a problem, please zip up your project and attach it here or email it to me ( [email protected]) so I can take a look.
Ben
http://abi.exdream.com
|
|
 |