so for my first project with MonoGame I decided to make a tetris clone but I have an issue which I don't know how to solve.
Currently my code is generating a block and moves it downwards until it reach a specific y position. The block need to stay at this position and a new block spawns. I'm doing this with a List which contains object of the block class and then just draw all the blocks in this list.
I took out parts which I believe are not involved in the problem:
public class PlayField : DrawableGameComponent
{
private Game1 gameRef;
private Texture2D fieldTexture;
private BlockGenerator blockGenerator;
private Texture2D[] allBlocks;
private Block currentBlock;
public bool[,] fieldFilled;
private int down_Blocks = 22;
private int side_Blocks = 10;
public List<Block> placedBlocks;
public PlayField(Game game) : base(game)
{
placedBlocks = new List<Block>();
allBlocks = new Texture2D[4];
blockGenerator = new BlockGenerator(allBlocks,gameRef);
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
try
{
if (currentBlock.isMoving == false)
{
placedBlocks.Add(currentBlock);
currentBlock = null;
currentBlock = blockGenerator.GenerateBlock();
}
else
{
currentBlock.UpdatePosition(gameTime);
if (InputManager.CheckForKeyBoardRelease(Keys.A))
{
currentBlock.MoveLeft();
}
if (InputManager.CheckForKeyBoardRelease(Keys.D))
{
currentBlock.MoveRight();
}
}
}
catch(NullReferenceException e)
{
currentBlock = blockGenerator.GenerateBlock();
}
}
public override void Draw(GameTime gameTime)
{
gameRef.SpriteBatch.Begin();
if(currentBlock != null)
{
currentBlock.DrawBlocks();
}
foreach(Block b in placedBlocks)
{
b.DrawBlocks();
}
gameRef.SpriteBatch.End();
base.Draw(gameTime);
}
The method "GenerateBlock" returns a object of type "Block"
public class Block : DrawableGameComponent
{
Game1 gameRef;
public Texture2D blockTexture;
public Vector2[] blockPositions;
TimeSpan lastMove;
TimeSpan blockMove = TimeSpan.FromMilliseconds(500);
public bool isMoving;
public Block(Game game, Texture2D _blockTexture, Vector2[] _blockPositions) : base(game)
{
gameRef = (Game1)game;
blockTexture = _blockTexture;
blockPositions = _blockPositions;
isMoving = true;
}
public void UpdatePosition(GameTime gameTime)
{
Vector2 bottomBlockPositon = FindBottomBlock();
if(bottomBlockPositon.Y < 550)
{
if (WaitTillMove(gameTime))
{
for (int i = 0; i < blockPositions.Length; i++)
{
blockPositions[i] = new Vector2(blockPositions[i].X, blockPositions[i].Y + 25);
}
}
}
else
{
isMoving = false;
Console.WriteLine("X: " +blockPositions[0].X + " Y:" + blockPositions[0].Y);
}
}
public Vector2 FindBottomBlock()
{
Vector2 result = new Vector2(0, 0);
for(int i = 0; i < blockPositions.Length; i++)
{
if(blockPositions[i].Y > result.Y)
{
result = blockPositions[i];
}
}
return result;
}
public bool WaitTillMove(GameTime gameTime)
{
if (lastMove + blockMove < gameTime.TotalGameTime)
{
lastMove = gameTime.TotalGameTime;
return true;
}
return false;
}
public void DrawBlocks()
{
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[0], Color.White);
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[1], Color.White);
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[2], Color.White);
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[3], Color.White);
}
}
Debugging says that my List contains an Element even though it has the wrong positions. But this shouldn't matter because I still only "see" one Block at the same time.
Hopefully someone can toss me into the right direction.
Edit:
public class BlockGenerator
{
Random random;
Texture2D[] allBlocks;
Vector2[] blockPositions;
Texture2D currentBlock;
BlockEnums currentBlockEnum;
Game1 gameRef;
public BlockGenerator(Texture2D[] blocks, Game1 game)
{
gameRef = (Game1)game;
allBlocks = blocks;
currentBlock = allBlocks[1];
blockPositions = new Vector2[4];
random = new Random();
}
public Block GenerateBlock()
{
int colorValue = random.Next(0, 4); //0 = blue, 1 = green, 2 = red, 3 = yellow
currentBlock = allBlocks[colorValue];
currentBlockEnum = BlockEnums.Line;
blockPositions[0] = new Vector2(100, 0);
blockPositions[1] = new Vector2(125, 0);
blockPositions[2] = new Vector2(150, 0);
blockPositions[3] = new Vector2(175, 0);
Block generatedBlock = new Block(gameRef,currentBlock, blockPositions);
return generatedBlock;
}
public class BlockGenerator
{
Random random;
Texture2D[] allBlocks;
Vector2[] blockPositions; // delete this
Then move the initialization code from the constructor into GenerateBlock (add var).
var blockPositions = new Vector2[4];
Then it should work. You were creating new vectors each time you created a block but re-using the same instance of blockPositions each time, so both blocks do exist but will always have the exact same positions.
I haven't had time to test but I think it's right...let me know :)
edit: I'd also suggest moving blockPositions entirely into the Block class. I see no value (in the code you have posted, maybe you have plans for later) in having that logic outside of the class that really owns it...and if it was contained within the class to begin with you would have avoided this problem. Just a suggestion :)
Related
I'm working on Unity3d project where the floor has to unfold gradually. I created a script FloorModule.cs where using coroutine the floor tiles are laying out gradually. Each next module has to unfold right after previous is completed. There for I created Spawner.cs to loop a new FloorModule.cs right after previous one is completed.
I can't seem to get my head around how to use coroutine to synchronize the mainloop (Spawner.cs) with subloop on prefab (FloorModule.cs).
Here is the link to the example
https://1drv.ms/u/s!AkVZpIE6f1GV4M5Ju7G5zPOrQcCe8w?e=QrghRT
P.S.
In given example, as loop goes forward I'm using "Reference.cs" class to change some variable values .
FloorModule.cs
public class FloorModule : MonoBehaviour
{
public float zSpacer = 0f;
public int instPrefabCount;
public Transform spawnPoint;
public int lenght = 15;
public int width = 5;
public GameObject floorTiles;
void Start()
{
spawnPoint = GetComponent<Transform>();
StartCoroutine(FwFloorDelay(spawnPoint));
}
public IEnumerator FwFloorDelay(Transform origin)
{
for (int l = 0; l < lenght; l++)
{
float xAngle = 90;
float yPos = 0;
float zPos = 0 + l;
for (int w = 0; w < width; w++)
{
int xSelection = Random.Range(0, 6);
GameObject xFloor = Instantiate(floorTiles, origin);
TileStatusNames(xFloor, l, w);
// defining positiona and angles
float xPos = w + (zSpacer * w);
xFloor.transform.localEulerAngles = new Vector3(xAngle, 0, 0);
xFloor.transform.localPosition = new Vector3(xPos, yPos, zPos);
yield return new WaitForSeconds(.05f);
}
}
Spawner.cs
public class Spawner : MonoBehaviour
{
public GameObject FloorModPrefab;
public References[] referenceScript;
void Start()
{
StartCoroutine(SpawnModules());
}
IEnumerator SpawnModules()
{
for (int i = 0; i < referenceScript.Length; i++)
{
referenceScript[i].instance =
Instantiate(FloorModPrefab, referenceScript[i].ref_spawnPoint.position, referenceScript[i].ref_spawnPoint.rotation);
referenceScript[i].ref_instFloorModCount = i + 1;
referenceScript[i].Setup();
yield return new WaitForSeconds(5f);
}
}
}
References.cs
[Serializable]
public class References
{
FloorModule prefabObjScript;
public GameObject instance;
public int ref_instFloorModCount;
public Transform ref_spawnPoint;
public int ref_Width = 5;
public int ref_Lenght = 15;
public void Setup()
{
// Get references to the components.
prefabObjScript = instance.GetComponent<FloorModule>();
// Set the player numbers to be consistent across the scripts.
prefabObjScript.instPrefabCount = ref_instFloorModCount;
prefabObjScript.spawnPoint = ref_spawnPoint;
prefabObjScript.width = ref_Width;
prefabObjScript.lenght = ref_Lenght;
}
}
I tried to use coroutines unfortunately in given context I realize it's impossible for me to resolve this task.
You can yield a coroutine from within another coroutine.
Changes to your Code
In References change public GameObject instance; to public FloorModule instance;
In Spawner change public GameObject FloorModPrefab; to public FloorModule FloorModPrefab;
Remove the code from Start of FloorModule.
Modify FwFloorDelay to
public IEnumerator FwFloorDelay(Transform origin = null)
{
if (origin == null)
{
origin = transform;
}
...
}
In SpawnModules, chain the floor delay coroutine
IEnumerator SpawnModules()
{
for (int i = 0; i < referenceScript.Length; i++)
{
...
yield return referenceScript[i].instance.FwFloorDelay();
}
}
Your goal should not be to synchronize separate coroutines, but rather to get the code running sequentially in one coroutine.
For example, you could make References.Setup() asynchronous, and then have it invoke FloorModel.FwFloorDelay directly instead of the FloorModel starting its own separate coroutine.
as the titel say's I'm trying to make a game in Monogame, a Bomberman clone, but atm when I try to spawn more then one bomb the animation just moves to the new location. The game object stays where it is supposed to, but it does not have the animation. I think the problem is that it loads the same animation which has been loaded but I just cant seem to figure out how to get it to spawn a new animation everytime a bomb spawns.
I use Content.Load to get the sorite animation, and spawn the bomb with a clone() function with the animation.
var bombAni = new Dictionary<string, Animation>() {
{"Bomb", new Animation(Content.Load<Texture2D>("Obstacle/Bomb"), 3) },
};
_sprites = new List<Sprite>() {
new Player(animations) {
_bomb = new Bomb(bombAni),
Position = new Vector2(32, 32),
Input = new Input() {
Up = Keys.W,
Down = Keys.S,
Left = Keys.A,
Right = Keys.D,
Space = Keys.Space,
}
}
};
public void Play(Animation animation) {
if (animation == _animation) {
return;
}
_animation = animation;
_animation.CurFrame = 0;
_timer = 0;
}
private void AddBomb(List<Sprite> sprites) {
var bomb = _bomb.Clone() as Bomb;
switch (_direction) {
case "Up":
bomb.Position = _position + new Vector2(0, -32);
break;
case "Down":
bomb.Position = _position + new Vector2(0, 32);
break;
case "Left":
bomb.Position = _position + new Vector2(-32, 0);
break;
case "Right":
bomb.Position = _position + new Vector2(32, 0);
break;
}
bomb.lifeSpan = 3f;
bomb.Parent = this;
bombCount++;
sprites.Add(bomb);
}
public Sprite(Dictionary<string, Animation> animations) {
_animations = animations;
_animationManager = new AnimationManager(_animations.First().Value);
}
namespace CompetenceWeek.scripts {
public class Bomb : Sprite {
public float lifeSpan;
private float _timer;
public Bomb(Dictionary<string, Animation> animations) : base(animations) {
}
public Bomb(Texture2D texture) : base(texture) {
}
public override void Update(GameTime gameTime, List<Sprite> sprites) {
_timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
SetAnimations();
_animationManager.Update(gameTime);
if (_timer > lifeSpan) {
isDead = true;
}
}
}
}
namespace CompetenceWeek.scripts {
public class Sprite : ICloneable {
protected AnimationManager _animationManager;
protected Dictionary<string, Animation> _animations;
protected Vector2 _position;
protected Texture2D _texture;
public bool Walkable { get; set; } = false;
public bool isDead = false;
public Player Parent { get; set; }
public Input Input;
public Vector2 Position {
get { return _position; }
set {
_position = value;
if(_animationManager != null) {
_animationManager.Posistion = _position;
}
}
}
public float Speed = 2.5f;
public Vector2 Velocity;
public virtual void Draw(SpriteBatch spriteBatch) {
if(_texture != null) {
spriteBatch.Draw(_texture, Position, Color.White);
} else if (_animationManager != null) {
_animationManager.Draw(spriteBatch);
} else {
throw new Exception("somthings not right");
}
}
public Sprite(Dictionary<string, Animation> animations) {
_animations = animations;
_animationManager = new AnimationManager(_animations.First().Value);
}
public Sprite(Texture2D texture) {
_texture = texture;
}
public virtual void Update(GameTime gameTime, List<Sprite> sprites) {
SetAnimations();
_animationManager.Update(gameTime);
Position += Velocity;
Velocity = Vector2.Zero;
}
protected virtual void SetAnimations() {
if (Velocity.X > 0) {
_animationManager.Play(_animations["PlayerRight"]);
} else if (Velocity.X < 0) {
_animationManager.Play(_animations["PlayerLeft"]);
} else if (Velocity.Y > 0) {
_animationManager.Play(_animations["PlayerDown"]);
} else if (Velocity.Y < 0) {
_animationManager.Play(_animations["PlayerUp"]);
}
}
public object Clone() {
return this.MemberwiseClone();
}
}
}
The problem is with this segment:
protected Dictionary<string, Animation> _animations;
public object Clone() {
return this.MemberwiseClone();
}
When you perform a memberwise clone, it creates a shallow copy, reusing existing references to reference-type objects. This means that the Clone will share the same animation state with the original object.
The solution to this, is to instantiate a new copy of all of the Animations every time you want to clone Bomb.
Something like this:
public object Clone() {
var bomb = (Bomb)this.MemberwiseClone();
bomb.InitAnimations(new Dictionary<string, Animation>() {
{"Bomb", new Animation(Content.Load<Texture2D>("Obstacle/Bomb"), 3) },
};
return bomb;
}
I am creating a 2D game and I've hit a serious road block when trying to 'randomly' spawn power ups. Basically what I am attempting to do is spawn the power ups off screen and then they move onto the screen (at the same speed as my scrolling background) Once one of the three power ups has been spawned another one won't spawn until 10 to 30 seconds has passed. I also understand that the shield power Up is the only one that I am trying to get spawning randomly at the moment. I have been researching for hours and writing code that I will post below. I have tried to do this all by myself so I apologize for the quality of the code, I am still a novice and learning. Any help or links to websites would be greatly appreciated as I have no idea where to proceed from here.
Thanks in advance.
game1.cs
public List<PowerUps> powerUpList = new List<PowerUps>();
public double counterPower = 0;
public bool powerUpCollision = false;
public bool invincibility = false;
Sprite shieldPower;
Sprite infiniteAmmoPower;
Sprite livesPower;
bool isVisible = true;
public bool infiniteAmmoBool = false;
public bool infiniteAmmoCol = false;
protected override void Initialize()
{
shieldPower = new Sprite();
infiniteAmmoPower = new Sprite();
livesPower = new Sprite();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
shieldPower.LoadContent(this.Content, "powerUpShield");
infiniteAmmoPower.LoadContent(this.Content, "infinitePowerUp");
livesPower.LoadContent(this.Content, "livePowerUp");
}
protected override void Update(GameTime theGameTime)
{
}
public void powerUps(GameTime theGameTime)
{
//if (mPlayer.boundingBox.Intersects(shieldPower.boundingBoxShieldPower))
{
Console.WriteLine(shieldPower.Position.X);
if (shieldPower.isVisible == true)
{
Console.WriteLine("collision working");
powerUpCollision = true;
invincibility = true;
shieldPower.isVisible = false;
if (powerUpCollision == true && invincibility == true)
{
lives = lives - 0;
}
counterPower = 0;
}
}
counterPower += theGameTime.ElapsedGameTime.TotalSeconds;
{
// Console.WriteLine(counterPower);
if (counterPower > 7)
{
powerUpCollision = false;
invincibility = false;
}
}
if (mPlayer.boundingBox.Intersects(livesPower.boundingBoxLives))
{
if (livesPower.isVisible == true)
{
lives = lives + 1;
livesPower.isVisible = false;
}
}
}
public void loadPowerUps()
{
int randPowerX = random.Next(850, 1400); // spawns randomly between these coordinates
int randPowerY = random.Next(-300, 300);
Console.WriteLine(shieldPower.Position.X);
if (powerUpList.Count < 1) // limits the powerUp count to 1 at a time
{
powerUpList.Add(new PowerUps(Content.Load<Texture2D>("powerUpShield"), new Vector2(randPowerX, randPowerY)));
}
for (int i = 0; i < powerUpList.Count; i++)
{
if (powerUpList[i].isVisible == false)
{
powerUpList.RemoveAt(i);
i--;
}
}
}
protected override void Draw(GameTime theGameTime)
{
foreach (PowerUps powerup in powerUpList)
{
powerup.Draw(spriteBatch);
}
mPlayer.Draw(this.spriteBatch);
enemyShip.Draw(this.spriteBatch);
if (shieldPower.isVisible == true)
{
shieldPower.Draw(this.spriteBatch);
}
if (livesPower.isVisible == true)
{
livesPower.Draw(this.spriteBatch);
}
if (infiniteAmmoPower.isVisible == true)
{
infiniteAmmoPower.Draw(this.spriteBatch);
}
}
PowerUps.cs
public class PowerUps : Game1
{
Random randomPower = new Random();
int minPowerSpawnTimer = 10000; // 10 seconds
int maxPowerSpawnTimer = 30000; // 30 seconds
double nextSpawnTime;
float powerUpSpeed = -0.5f;
Vector2 position;
Texture2D texture;
public PowerUps(Texture2D newText, Vector2 newPos)
{
position = newPos;
texture = newText;
}
public void Update(GameTime theGameTime)
{
position.X += powerUpSpeed;
nextSpawnTime -= theGameTime.ElapsedGameTime.Milliseconds;
if (nextSpawnTime < 0)
{
loadPowerUps();
resetSpawnTime();
}
}
public void resetSpawnTime()
{
nextSpawnTime = randomPower.Next(minPowerSpawnTimer, maxPowerSpawnTimer);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.White);
}
}
Your problem is the class structure you've created. Your should not be inheriting Game1 in your PowerUps class. It is creating multiple instances of Game1 objects each of which has its own list of PowerUps.
It doesn't appear to me that any of the Update() methods in the PowerUps instances will ever be called.
I think you may be wanting to access functionallity of your Game1 class from your PowerUps class. Inheriting (PowerUps : Game1) is not the way to do it here.
Instead, make a PowerUps class that does not inherit anything. Then from your Game1 Update method, call your PowerUps instances' Update method and pass what variables it needs. Same with the drawing.
In order for your Update method to be called, your PowerUps class must inherit from GameComponent, not Game1.
Since you also have a Draw method, to have your Draw method also called automatically your must inherit from DrawableGameComponent instead.
Those base classes take your Game1 as a parameter, so you will have to modify your constructor:
public class PowerUps : DrawableGameComponent
{
//Variables
public PowerUps(Texture2D newText, Vector2 newPos, Game1 game) : base(game)
{
position = newPos;
texture = newText;
}
Now that you have a GameComponent, you must register it in your Game1 class upon creation. So, when creating your power ups in your powerUpList, change your code from
powerUpList.Add(new PowerUps(Content.Load<Texture2D>("powerUpShield"), new Vector2(randPowerX, randPowerY)));
to:
PowerUps powerUp = new PowerUps(Content.Load<Texture2D>("powerUpShield"), new Vector2(randPowerX, randPowerY))
powerUpList.Add(powerUp);
Components.Add(powerUp);
When your component is registered within the Components list, the Game can start calling the Draw and Update methods of your component. Once you're finish with it, remove it from the list.
The game stopped working when the following function is called:
The error is: shotmanager object wont be passed and always will be null! I was following a tutorial and did exactly as he did!
I clearly initiated it.
Some coding here
namespace SpaceShip
{
class Enemy: Sprite
{
public shotmanager shotmanager;
private double timesincelastshot;
private const int timedelay = 1;
private Vector2 pos;
public Enemy(Texture2D Text, Vector2 VEC, Rectangle REC, shotmanager shotmanager)
: base(Text, VEC, REC)
{
shipspeed = 300;
this.shotmanager = shotmanager;
}
public override void Update(KeyboardState keyboard, GameTime gameTime)
{
var random = new Random();
if (Velocity == Vector2.Zero)
{
var direction = random.Next(2);
Velocity = new Vector2(direction == 0 ? -1 : 1, 0);
}
else if (gameTime.ElapsedGameTime.Seconds % 2 == 0)
{
if (random.Next(15) == 0)
Velocity = new Vector2(-velocity.X, velocity.Y);
}
timesincelastshot += gameTime.ElapsedGameTime.TotalSeconds;
if (timesincelastshot > timedelay)
{
if (random.Next(2) == 0)
pos = calculateposition();
shotmanager.fireenemyshot(pos);
timesincelastshot = 0;
}
base.Update(keyboard, gameTime);
}
private Vector2 calculateposition()
{
return VEC + new Vector2(TEXT.Width/2, TEXT.Height/2);
}
}
}
namespace SpaceShip
{
public class shotmanager
{
private Shot shot;
public Texture2D shottexture;
private Rectangle bounds;
private List<Shot> shotgroup = new List<Shot>();
//public shooting shot;
Vector2 vec;
public shotmanager(Texture2D shottexture, Rectangle bounds)
{
// TODO: Complete member initialization
this.shottexture = shottexture;
this.bounds = bounds;
}
public void fireenemyshot(Vector2 shotposition)
{
var inflatebounds = bounds;
vec = shotposition;
inflatebounds.Inflate(10, 10);
shot.Velocity = new Vector2(0, 1);
shotgroup.Add(shot);
shot = new Shot(shottexture, shotposition, inflatebounds);
}
public void draw(SpriteBatch spriteBatch)
{
foreach (var i in shotgroup)
shot.draw(spriteBatch);
}
public void Update(KeyboardState keyboard, GameTime gameTime)
{
foreach (var i in shotgroup)
shot.Update(keyboard, gameTime);
}
}
}
namespace SpaceShip
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Sprite background;
shipclass spaceship;
//Sprite spaceship;
SpriteFont score;
EnemyManger enemy;
Texture2D shottexture;
public shotmanager shotmanager;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <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
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.
var alienship = Content.Load<Texture2D>("flying_saucer_2");
Texture2D spaceshiptexture;
spriteBatch = new SpriteBatch(GraphicsDevice);
spaceshiptexture = Content.Load<Texture2D>("1358114942_kspaceduel");
var positionx = (graphics.GraphicsDevice.Viewport.Width - spaceshiptexture.Width) / 2;
var positiony = (graphics.GraphicsDevice.Viewport.Height - spaceshiptexture.Height );
var ship = new Rectangle(0, graphics.GraphicsDevice.Viewport.Height - spaceshiptexture.Height -150 , graphics.GraphicsDevice.Viewport.Width, spaceshiptexture.Height+150);
background = new Sprite(Content.Load<Texture2D>("large_space_1920x1200"), Vector2.Zero, graphics.GraphicsDevice.Viewport.Bounds);
spaceship = new shipclass(spaceshiptexture, new Vector2(positionx, positiony), ship);
score = Content.Load<SpriteFont>("SpriteFont1");
shottexture = Content.Load<Texture2D>("64px-SpaceInvadersLaserDepiction");
shotmanager = new shotmanager(shottexture, graphics.GraphicsDevice.Viewport.Bounds);
enemy = new EnemyManger(alienship, graphics.GraphicsDevice.Viewport.Bounds, shotmanager);
public void fireenemyshot(Vector2 shotposition)
{
var inflatebounds = bounds;
vec = shotposition;
inflatebounds.Inflate(10, 10);
// CULPRIT HERE
shot.Velocity = new Vector2(0, 1);
shotgroup.Add(shot);
// THIS IS DONE TOO LATE.
shot = new Shot(shottexture, shotposition, inflatebounds);
}
The problem is that you are attempting to set the Velocity on shot, but you have never instantiated it before here. You are instantiating it after you are attempting to use.
EDIT - FYI The pos that you are passing into this method from Enemy is also never set. It's a struct so it should be initialized, however, you are never giving it a value. Nevermind, I saw where it gets set.
I'm learning to use the as operator, and my goal was to create an option window (non windows form) that can:
Have options added to it (for flexibility, in case I want to use if statements to add menu items)
Be able to display text, textures, or a class (using the classes draw function)
Be controlled through the host GameState
I still haven't added the options for indicating an item is selected, my apologies for not posting a complete work. I also have not sorted the code into regions yet. Sorry again!
Is my code (particularly the draw function) properly using the is and as operators properly, from a performance and readability (non spaghetti code) standpoint?
public class OptionWindow : DrawableGameComponent
{
public Dictionary<int, Option> options;
int selectedOption;
bool windowLoops;
Rectangle drawRectangle;
int spacer;
int totalItemHeight;
SpriteFont sf;
SpriteBatch sb;
public Rectangle DrawRectangle
{
get { return drawRectangle; }
set { drawRectangle = value; }
}
public int SelectedOption
{
get { return selectedOption; }
set
{
if (windowLoops)
{
if (selectedOption >= options.Count())
selectedOption = 0;
if (selectedOption < 0)
selectedOption = options.Count() - 1;
}
else
{
if (selectedOption >= options.Count())
selectedOption = options.Count() - 1;
if (selectedOption < 0)
selectedOption = 0;
}
}
}
public OptionWindow(Game game, bool windowLoops, SpriteFont sf, Rectangle drawRectangle)
: base(game)
{
options = new Dictionary<int, Option>();
this.windowLoops = windowLoops;
this.sf = sf;
DrawRectangle = new Rectangle(drawRectangle.X, drawRectangle.Y, drawRectangle.Width, drawRectangle.Height);
}
public void Add(object option, bool selectable, bool defaultSelection, int height)
{
options.Add(options.Count(), new Option(selectable, option, height));
if (defaultSelection)
SelectedOption = options.Count() - 1;
UpdatePositions();
}
public void UpdatePositions()
{
UpdateTotalItemHeight();
if (options.Count() - 1 != 0)
spacer = (drawRectangle.Height - totalItemHeight) / (options.Count() - 1);
for (int i = 0; i < options.Count(); i++)
{
if (i == 0)
options[i].Position = new Vector2(drawRectangle.X, drawRectangle.Y);
else
{
options[i].Position = new Vector2(
drawRectangle.X,
options[i - 1].Position.Y + options[i - 1].Height + spacer);
}
}
}
public void UpdateTotalItemHeight()
{
totalItemHeight = 0;
for (int i = 0; i < options.Count(); i++)
{
totalItemHeight += options[i].Height;
}
}
protected override void LoadContent()
{
sb = new SpriteBatch(GraphicsDevice);
base.LoadContent();
}
public override void Draw(GameTime gameTime)
{
for (int i = 0; i < options.Count(); i++)
{
if (options[i].OptionObject is string)
sb.DrawString(sf, options[i].OptionObject as string, options[i].Position, Color.White);
if (options[i].OptionObject is Texture2D)
sb.Draw(options[i].OptionObject as Texture2D,
new Rectangle(
(int)options[i].Position.X,
(int)options[i].Position.Y,
options[i].Height,
(options[i].Height / (options[i].OptionObject as Texture2D).Height) * (options[i].OptionObject as Texture2D).Width),
Color.White);
if (options[i].OptionObject is DisplayObject)
(options[i].OptionObject as DisplayObject).Draw(gameTime);
}
base.Draw(gameTime);
}
}
public class Option
{
bool selectable;
object optionObject;
int height;
Vector2 position;
public bool Selectable
{
get { return selectable; }
set { selectable = value; }
}
public object OptionObject
{
get { return optionObject; }
set { optionObject = value; }
}
public int Height
{
get { return height; }
set { height = value; }
}
public Vector2 Position
{
get { return position; }
set { position = value; }
}
public Option(bool selectable, object option, int height)
{
Selectable = selectable;
OptionObject = option;
Height = height;
}
}
It is never adviseable to use is and then as. The usual way to go would be to either of the following:
just use is (if you just want to know the type without subsequent casting)
assign the result of as to a variable and check whether that variable is (not) null
The code analysis tool FxCop helps you find any spots in your code that use is and then as and warns you because of performance concerns.
Note however that a better approach altogether might be to declare your OptionObject property as some abstract class with a Draw method. You could then derive a subclass for strings, one for Texture2D instances and another one for DisplayObject instances and just call Draw in your OptionWindow.Draw method. This would leave the decision which actual drawing operations to execute up to built-in polymorphism features of the framework.