Sorry, I wasn't specific enough in my initial post. I am making a 2d side scrolling space shooter, but I am doing it in 3d to leverage the effects of 3d. So my models that I render will be in the foreground while the particles will be effectively in the background.
As far as ridiculous I initialize the particles to have a position in the Z axis of 9000, where as the models are something on the order of 100 I believe.
I am currently not using point sprites but rendering pixels that correlate to the vertex points.
I will provide the full source since there are only a couple of classes currently
Particle.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;namespace CowboyBebop{ class Particle{ public float angle; public float velocity; //public float velocity0; //public float velocityy; public Vector3 position; public Vector3 position0; public Color color; public int life; public double time0; public int type; //public int pointSize; public enum ptype { STARFIELD, EXPLOSION, PROJECTILE } public static float gravity = 9.81f; public Particle(){ this.angle = 0.0f; //this.velocity0 = 1.0f; //this.velocityy = this.velocity0; this.velocity = 1.0f; this.position = new Vector3(0.0f, 0.0f, 0.0f); this.position0 = new Vector3(0.0f, 0.0f, 0.0f); this.color = Color.White; this.life = 0; this.time0 = 0.0f; this.type = (int)ptype.EXPLOSION; //this.pointSize = 1; } public Particle(float xpos, float ypos, float zpos, Color c1, float v, float a){ this.angle = a; //this.velocity0 = v; //this.velocityy = v; this.velocity = v; this.color = c1; this.position = new Vector3(xpos, ypos, zpos); this.position0 = new Vector3(xpos, ypos, zpos); this.life = 0; this.time0 = 0.0f; this.type = (int)ptype.EXPLOSION; //this.pointSize = 1; } public void age(){ if (this.life > 0){ this.life--; } } public void kill(){ this.life = 0; } public Boolean isAlive(){ if (this.life > 0){return true;} else{return false;} } public void move(){ this.position.X = this.position.X + ((float)Math.Sin(MathHelper.ToRadians(angle))) * this.velocity; this.position.Y = this.position.Y + ((float)Math.Cos(MathHelper.ToRadians(angle))) * this.velocity; } public void projectileMove(double time){ this.position0 = this.position; float currentTime = (float)(time - this.time0); this.position.Y = this.position0.Y + (((this.velocity * (float)Math.Cos(angle)) * currentTime) + ((Particle.gravity * (currentTime * currentTime))) / 2.0f); this.position.X = this.position0.X + ((this.velocity * (float)Math.Sin(angle)) * currentTime); } }}
ParticleManager.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework;namespace CowboyBebop{ class ParticleManager{ private Particle[] particleList; private int totalP; private Random random; private GraphicsDeviceManager graphics; private VertexPositionColor[] pointList; private int width; private int height; private Matrix worldMatrix; private Matrix viewMatrix; private Matrix projectionMatrix; private VertexDeclaration basicEffectVertexDeclaration; private VertexBuffer vertexBuffer; private BasicEffect basicEffect; public ParticleManager(GraphicsDeviceManager g) { this.graphics = g; this.width = graphics.PreferredBackBufferWidth; this.height = graphics.PreferredBackBufferHeight; this.totalP = 1000; this.pointList = new VertexPositionColor[this.totalP]; this.particleList = new Particle[this.totalP]; this.random = new Random(); for (int i = 0; i < this.totalP; i++){ this.particleList = new Particle(); } initGraphics(); } public ParticleManager(int total, GraphicsDeviceManager g) { this.graphics = g; this.width = graphics.PreferredBackBufferWidth; this.height = graphics.PreferredBackBufferHeight; this.totalP = total; this.pointList = new VertexPositionColor[this.totalP]; this.particleList = new Particle[totalP]; this.random = new Random(); for (int i = 0; i < this.totalP; i++){ this.particleList = new Particle(); } initGraphics(); } private void initGraphics(){ float tilt = MathHelper.ToRadians(0f); // 0 degree angle // Use the world matrix to tilt the cube along x and y axes. worldMatrix = Matrix.CreateRotationX(tilt) * Matrix.CreateRotationY(tilt); viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 10000.0f),Vector3.Zero,Vector3.Up); projectionMatrix = Matrix.CreateOrthographicOffCenter( 0, (float)graphics.GraphicsDevice.Viewport.Width, (float)graphics.GraphicsDevice.Viewport.Height, 0, 1.0f, 10000.0f); basicEffect = new BasicEffect(graphics.GraphicsDevice, null); basicEffect.Alpha = 1.0f; basicEffect.VertexColorEnabled = true; basicEffect.World = worldMatrix; basicEffect.View = viewMatrix; basicEffect.Projection = projectionMatrix; vertexBuffer = new VertexBuffer(graphics.GraphicsDevice, VertexPositionColor.SizeInBytes * this.totalP, BufferUsage.None ); vertexBuffer.SetData<VertexPositionColor>(pointList); basicEffectVertexDeclaration = new VertexDeclaration( graphics.GraphicsDevice, VertexPositionColor.VertexElements); } public void update(double time){ boundsCheck(); age(); updatePointList(); move(time); } private void move(double time){ int count = 0; int type = (int)Particle.ptype.EXPLOSION; for (int i = 0; i < this.totalP; i++){ if (this.particleList.isAlive()){ type = this.particleList.type; switch (type){ case (int)Particle.ptype.PROJECTILE: this.particleList.projectileMove(time); break; case (int)Particle.ptype.EXPLOSION: this.particleList.move(); break; case (int)Particle.ptype.STARFIELD: this.particleList.move(); break; default: this.particleList.move(); break; } this.pointList[count].Position.X = this.particleList.position.X; this.pointList[count].Position.Y = this.particleList.position.Y; this.pointList[count].Position.Z = this.particleList.position.Z; count++; } } } private void age(){ for (int i = 0; i < this.totalP; i++){ if (this.particleList.isAlive()){ if (this.particleList.type != (int)Particle.ptype.STARFIELD){ this.particleList.age(); } } } } public void killAll(){ for (int i = 0; i < this.totalP; i++){ this.particleList.kill(); } } public void addExplosion(int total, float x, float y, double time) { int count = 0; for (int i = 0; i < this.totalP; i++){ if (!this.particleList.isAlive()){ if (count < total){ this.particleList.position0.X = x; this.particleList.position0.Y = y; this.particleList.position0.Z = 0; this.particleList.position.X = x; this.particleList.position.Y = y; this.particleList.position.Z = 0; this.particleList.time0 = time; this.particleList.angle = (float)(this.random.NextDouble() * 359); this.particleList.velocity = (float)(this.random.NextDouble() * 10) + 0.1f; this.particleList.color = getExlosionColor(); this.particleList.life = this.random.Next(50); this.particleList.type = (int)Particle.ptype.EXPLOSION; count++; } } } } public void addProjectileCloud(int total, float x, float y, double time) { int count = 0; for (int i = 0; i < this.totalP; i++) { if (!this.particleList.isAlive()) { if (count < total) { this.particleList.position0.X = x; this.particleList.position0.Y = y; this.particleList.position0.Z = 9000; this.particleList.position.X = x; this.particleList.position.Y = y; this.particleList.position.Z = 9000; this.particleList.time0 = time; this.particleList.angle = (float)(this.random.NextDouble() * 359); this.particleList.velocity = (float)(this.random.NextDouble() * 3) + 0.1f; this.particleList.color = getExlosionColor(); this.particleList.life = this.random.Next(200); this.particleList.type = (int)Particle.ptype.PROJECTILE; count++; } } } } public void initializeStarField(int totalStars){ int count = 0; for (int i = 0; i < this.totalP; i++){ if (!this.particleList.isAlive()){ if (count < totalStars){ this.particleList.position0.X = (float)(this.random.NextDouble() * this.width); this.particleList.position0.Y = (float)(this.random.NextDouble() * this.height); this.particleList.position0.Z = 9000; this.particleList.position.X = this.particleList.position0.X; this.particleList.position.Y = this.particleList.position0.Y; this.particleList.position.Z = 9000; this.particleList.time0 = 0.0f; this.particleList.angle = 270.0f; this.particleList.velocity = (float)(this.random.NextDouble() * 10) + 0.1f; this.particleList.color = Color.White; this.particleList.life = 1; this.particleList.type = (int)Particle.ptype.STARFIELD; count++; } } } } private Color getExlosionColor(){ int tmpN = random.Next(4); Color c = new Color(); switch (tmpN){ case 0: c = Color.White; break; case 1: c = Color.Red; break; case 2: c = Color.Orange; break; case 3: c = Color.Yellow; break; } return c; } public void render(){ graphics.GraphicsDevice.RenderState.PointSize = 2; graphics.GraphicsDevice.VertexDeclaration = basicEffectVertexDeclaration; graphics.GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes); // This code would go between a device // BeginScene-EndScene block. int tmpN = totalParticles(); if (tmpN > 0) { basicEffect.Begin(); foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) { pass.Begin(); graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.PointList, pointList, 0, tmpN); pass.End(); } basicEffect.End(); } } private void boundsCheck(){ for (int i = 0; i < this.totalP; i++){ if (this.particleList.isAlive()){ if ((this.particleList.position.X < 0) || (this.particleList.position.X > this.width)){ this.particleList.kill(); if (this.particleList.type == (int)Particle.ptype.STARFIELD){ this.particleList.position0.X = (float)this.width; this.particleList.position0.Y = (float)(this.random.NextDouble() * this.height); this.particleList.position0.Z = 9000; this.particleList.position.X = this.particleList.position0.X; this.particleList.position.Y = this.particleList.position0.Y; this.particleList.position.Z = 9000; this.particleList.life = 1; } } if ((this.particleList.position.Y < 0) || (this.particleList.position.Y > this.height)){ this.particleList.kill(); } } } } private void updatePointList(){ int tmpN = totalParticles(); int count = 0; this.pointList = new VertexPositionColor[tmpN]; for (int i = 0; i < this.totalP; i++) { if (this.particleList.isAlive()) { this.pointList[count].Position.X = this.particleList.position.X; this.pointList[count].Position.Y = this.particleList.position.Y; this.pointList[count].Position.Z = this.particleList.position.Z; this.pointList[count].Color = this.particleList.color; count++; } } } public int totalParticles(){ int tmpNum = 0; for (int i = 0; i < this.totalP; i++){ if (this.particleList.isAlive()){ tmpNum++; } } return tmpNum; } }}
GameObject.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Content;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework.Input;namespace CowboyBebop{ class GameObject{ public Model Model { get; set; } public Vector3 Position; //{ get; set; } public bool IsActive { get; set; } public BoundingSphere BoundingSphere { get; set; } public GameObject() { Model = null; Position = Vector3.Zero; IsActive = false; BoundingSphere = new BoundingSphere(); } public void LoadContent(ContentManager content, string modelName){ Model = content.Load<Model>(modelName); } public void Draw(Matrix view, Matrix projection) { Matrix translateMatrix = Matrix.CreateTranslation(Position); Matrix worldMatrix = translateMatrix; foreach (ModelMesh mesh in Model.Meshes){ foreach (BasicEffect effect in mesh.Effects){ effect.World = worldMatrix; effect.View = view; effect.Projection = projection; effect.EnableDefaultLighting(); effect.PreferPerPixelLighting = true; } mesh.Draw(); } } }}
Player.cs-In the process of moving all the rendering an position matrices into this class but haven't implemented it yet
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;namespace CowboyBebop{ class Player : GameObject{ public float Zoom = 200; public float RotationY = 0.0f; public float RotationX = 0.0f; public Matrix viewMatrix; public Matrix projectionMatrix; public Player(float apsectRatio) : base(){ projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), apsectRatio, 1.0f, 10000.0f); viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, Zoom), Vector3.Zero, Vector3.Up); } }}
Game.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 Microsoft.Xna.Framework.Net;using Microsoft.Xna.Framework.Storage;namespace CowboyBebop{ /// <summary> /// This is the main type for your game /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //Add our particle manager ParticleManager pm; MouseState old_mouse; private Player player; private float Zoom = 200; private float RotationY = 0.0f; private float RotationX = 0.0f; //private Matrix gameWorldRotation; private Matrix viewMatrix; private float aspectRatio; public Game1() { graphics = new GraphicsDeviceManager(this); IsMouseVisible = true; 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 viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, Zoom), Vector3.Zero, Vector3.Up); aspectRatio = graphics.GraphicsDevice.Viewport.Width / graphics.GraphicsDevice.Viewport.Height; player = new Player(aspectRatio); 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 pm = new ParticleManager(graphics); pm.initializeStarField(100); player.LoadContent(this.Content, "Models\\swordfish"); } /// <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 } /// <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) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here double t = gameTime.TotalGameTime.TotalSeconds; updateMouse(t); updateKeyboard(t); pm.update(t); base.Update(gameTime); } protected void updateMouse(double time) { MouseState current_mouse = Mouse.GetState(); if (current_mouse.LeftButton == ButtonState.Pressed) { if (old_mouse.LeftButton != ButtonState.Pressed) { //pm.addExplosion(100, (float)current_mouse.X, (float)current_mouse.Y, time); pm.addProjectileCloud(100, (float)current_mouse.X, (float)current_mouse.Y, time); } } old_mouse = current_mouse; } protected void updateKeyboard(double time){ KeyboardState newState = Keyboard.GetState(); // Is the SPACE key down? if (newState.IsKeyDown(Keys.A)){ Zoom++; } if (newState.IsKeyDown(Keys.Z)) { Zoom--; } if (newState.IsKeyDown(Keys.Right)){ player.Position.X++; } if (newState.IsKeyDown(Keys.Left)){ player.Position.X--; } if (newState.IsKeyDown(Keys.Down)){ player.Position.Y--; } if (newState.IsKeyDown(Keys.Up)){ player.Position.Y++; } //gameWorldRotation = Matrix.CreateRotationX(MathHelper.ToRadians(RotationX)) * //Matrix.CreateRotationY(MathHelper.ToRadians(RotationY)); viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, Zoom), Vector3.Zero, Vector3.Up); if (newState.IsKeyDown(Keys.Escape)){ Exit(); } } /// <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) { GraphicsDevice.Clear(Color.Black); // TODO: Add your drawing code here pm.render(); Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), aspectRatio, 1.0f, 10000.0f); player.Draw(viewMatrix, projection ); base.Draw(gameTime); } }}