I'm currently creating a 2D game engine in C#. At the moment, I'm implementing an entity component system.
My structure is as follows:
Entity Class: Contains a list of IGameComponent's, you can add, remove and delete any component by class type. (ie; entity.RemoveComponent(typeof(Transform));. It also contains a parent Entity, and a list child entities.
IGameComponent Interface: For now, is just an empty interface. (Note that: Components only contain data, and not functionality)
Entity Pool: Contains a list of all the active objects in the game, it's also used to create and destroy entities.
Everything so far is great.
However, I'm faced with a problem. Since components only contain data I need a way to initialize, update and render the components and I'd rather not just add a bunch of virtual methods to a GameComponent class, however I don't know of any other way to resolve it.
What are my options?
EDIT:
I've seen that Unity uses methods like 'SendMessage' which I can only assume uses reflection to call methods. Should I implement something similar?
I don't know if you still need it, but I have made something similar a few years ago and it might help you. It's written in C# built on top of MonoGame/XNA
You GameObject class can look like this
public class GameObject
{
List<GameObjectComponent> _goComponent = new List<GameObjectComponent();
public T GetComponent<T>() where T : GameObjectComponent
{
foreach (GameObjectComponent goc in _goComponent)
if (goc.GetType().Equals(typeof(T)))
return (T)goc;
return null;
}
public void AddComponent(GameObjectComponent gameObjectComponent)
{
_goComponent.Add(gameObjectComponent);
gameObjectComponent.gameObject = this;
gameObjectComponent.Init();
}
public virtual void Update(GameTime gameTime)
{
foreach (GameObjectComponent _goc in _goComponent)
_goc.Update(gameTime);
}
public static void Instantiate(GameObject gameObject)
{
Scene._AddedGO.Add(gameObject);
}
public static void Destroy(GameObject gameObject)
{
Scene._RemoveGO.Add(gameObject);
}
}
GameObjectComponent is similar to MonoBehaivior from Unity3D
public class GameObjectComponent
{
public GameObject gameObject;
public GameObjectComponent()
{
}
public virtual void Init()
{
}
public virtual void Update(GameTime gameTime)
{
}
}
and then you Inherit other classes like so:
public class Sprite : GameObjectComponent
{
public Texture2D texture;
public Vector2 origin = Vector2.Zero;
public Rectangle rect;
public Rectangle sourceRect;
public Color color = Color.White;
public float rotation = 0f;
private float layerDepth = 0f;
public int scale = 1;
public Sprite()
{
}
public void Load(string path)
{
texture = Setup.ContentDevice.Load<Texture2D>(path);
}
}
now you can finally create you Player GameObject
class Player : GameObjectComponent
{
float speed = 150f;
KeyboardState keyState;
float pos_X;
float pos_Y;
int rect_X;
int rect_Y;
public Player(float x, float y, int rx, int ry)
{
pos_X = x;
pos_Y = y;
rect_X = rx;
rect_Y = ry;
}
public override void Init()
{
Sprite sprite = new Sprite();
gameObject.AddComponent(sprite);
gameObject.GetComponent<Sprite>().Load("Sprites/MainGuySpriteSheet_0");
gameObject.GetComponent<Sprite>().scale = 1;
gameObject.GetComponent<Sprite>().rect = new Rectangle(46, 0, 32, 36);
Transform transform = new Transform();
gameObject.AddComponent(transform);
// gameObject.GetComponent<Transform>().position = new Vector2(Screen.width / 2 - gameObject.GetComponent<Sprite>().rect.Width, Screen.height / 2 - gameObject.GetComponent<Sprite>().rect.Height);
gameObject.GetComponent<Transform>().position = new Vector2(pos_X, pos_Y - 32 * (gameObject.GetComponent<Sprite>().scale - 1));
RectCollider collider = new RectCollider();
gameObject.AddComponent(collider);
gameObject.GetComponent<RectCollider>().Set(gameObject.GetComponent<Sprite>(), gameObject.GetComponent<Transform>());
SpriteRenderer render = new SpriteRenderer();
gameObject.AddComponent(render);
gameObject.GetComponent<SpriteRenderer>().layer = 1;
gameObject.GetComponent<SpriteRenderer>().Set(gameObject.GetComponent<Sprite>());
}
public override void Update(GameTime gameTime)
{
//movex = transform.position.X -= 25 * gameTime.DeltaTime();
if (Keyboard.GetState().IsKeyDown(Keys.Left))
gameObject.GetComponent<Transform>().Move(-speed * gameTime.DeltaTime(), 0);
else if (Keyboard.GetState().IsKeyDown(Keys.Right))
gameObject.GetComponent<Transform>().Move(speed * gameTime.DeltaTime(), 0);
else if (Keyboard.GetState().IsKeyDown(Keys.Down))
gameObject.GetComponent<Transform>().Move(0, speed * gameTime.DeltaTime());
else if (Keyboard.GetState().IsKeyDown(Keys.Up))
gameObject.GetComponent<Transform>().Move(0, -speed * gameTime.DeltaTime());
if (Keyboard.GetState().IsKeyDown(Keys.Space) && !keyState.IsKeyDown(Keys.Space))
{
GameObject tomato = new GameObject();
tomato.AddComponent(new Tomato());
tomato.GetComponent<Transform>().position = gameObject.GetComponent<Transform>().position;
GameObject.Instantiate(tomato);
}
if (Keyboard.GetState().IsKeyDown(Keys.Q) && !keyState.IsKeyDown(Keys.Q))
{
SceneManager.LoadScene(new AnotherOne());
}
keyState = Keyboard.GetState();
gameObject.GetComponent<Transform>().position.Y = MathHelper.Clamp(gameObject.GetComponent<Transform>().position.Y, 0, Screen.bounds.Height - gameObject.GetComponent<Sprite>().rect.Height * gameObject.GetComponent<Sprite>().scale);
gameObject.GetComponent<Transform>().position.X = MathHelper.Clamp(gameObject.GetComponent<Transform>().position.X, 0, Screen.bounds.Width - gameObject.GetComponent<Sprite>().rect.Width * gameObject.GetComponent<Sprite>().scale);
}
}
I hope it's not too confusing and helps you a little bit.
To make it clearer I leave a link to the git here: https://github.com/Memorix101/MonoGame_ComponentSystem
Cheers, Memorix101 :)
Related
I want to change between 2 materials, depending on the platforms (gameobject) rotation.
Here is what I've done so far:
public class PlatformSpawner : MonoBehaviour
{
public GameObject platform;
public Material[] platformMaterial;
Material currentMaterial;
Renderer _renderer;
}
void Start()
{
_renderer = this.GetComponent<Renderer>();
}
I also wrote this, but I don't want to change materials by buttons:
public void LeftTurn()
{
_renderer.material = platformMaterial[0];
currentMaterial = _renderer.material;
}
public void RightTurn()
{
_renderer.material = platformMaterial[1];
currentMaterial = _renderer.material;
}
}
And this is where the platform rotates randomly 90 degrees to the left or to the right:
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public void Step(float distance)
{
if (Random.value < 0.5)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0); //change to one of the materials
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0); //change to the other of the materials.
//This is where I want to material to switch.
//When the objects position changes, the material changes too.
}
}
}
There is a picture of the gameplay. I want to change the material of all the corner platforms to have a nice curve line view.
Can anyone help me what and how to do in this case? I am a bit lost there.
Every help is highly appreciated!
EDIT: new code looks like that. The only issue is that Unity gives me 15 errors (see on the picture below), even if Visual Studio says no issue has been found. The errors refer to the switch.
public class PlatformSpawner : MonoBehaviour
{
public GameObject platform;
public Transform lastPlatform;
SpawnPoint _spawn;
bool stop;
public Material straightMaterial;
public Material turnLeftMaterial;
public Material turnRightMaterial;
public Renderer roadPrefab;
[System.Serializable]
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public RoadType type;
public enum RoadType
{
Straight,
LeftTurn,
RightTurn
}
private enum Direction
{
Z,
X,
}
private Direction lastDirection;
public void Step(float distance)
{
type = RoadType.Straight;
if (Random.value < 0.5f)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0);
if (lastDirection == Direction.Z)
{
type = RoadType.RightTurn;
}
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0);
if (lastDirection == Direction.X)
{
type = RoadType.LeftTurn;
}
lastDirection = Direction.Z;
}
}
}
void Start()
{
_spawn.position = lastPlatform.position;
_spawn.orientation = transform.rotation;
StartCoroutine(SpawnPlatforms());
}
IEnumerator SpawnPlatforms()
{
while (!stop)
{
var _spawn = new SpawnPoint();
for (var i = 0; i < 20; i++)
{
var newPlatform = Instantiate(roadPrefab, _spawn.position, _spawn.orientation);
_spawn.Step(1.5f);
var roadMaterial = _spawn.type switch
{
SpawnPoint.RoadType.LeftTurn => turnLeftMaterial,
SpawnPoint.RoadType.RightTurn => turnRightMaterial,
_ => straightMaterial
};
newPlatform.GetComponent<Renderer>().material = roadMaterial;
yield return new WaitForSeconds(0.1f);
}
}
}
}
So it sounds like you basically have a working system for switching the materials and spawning you road parts and materials already look correctly according to your rotations - now you only need to identify the curves.
Actually this is pretty simple:
is the current part in X direction and the next will be in Z -> Left Turn
is the current part in Z direction and the next will be in X -> RightTurn
any other case is straight
So you could probably do something like
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public RoadType type;
public enum RoadType
{
Straight,
LeftTurn,
RightTurn
}
private enum Direction
{
// since your orientation by default equals the Z direction use that as default value for the first tile
Z,
X,
}
private Direction lastDirection;
public void Step(float distance)
{
type = RoadType.Straight;
if (Random.value < 0.5f)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0);
if(lastDirection == Direction.Z)
{
type = RoadType.RightTurn;
}
lastDirection = Direction.X;
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0);
if(lastDirection == Direction.X)
{
type = RoadType.LeftTurn;
}
lastDirection = Direction.Z;
}
}
}
And you didn't show your spawn code but I would assume something like
public class Example : MonoBehaviour
{
public Material straightMaterial;
public Material turnLeftMaterial;
public Material turnRightMaterial;
public Renderer roadPrefab;
private void Awake()
{
var spawnPoint = new SpawnPoint();
for(var i = 0; i < 20; i++)
{
var roadTile = Instantiate(roadPrefab, spawnPoint.position, spawnPoint.orientation);
// do the Step after spawning the current tile but before assigning the material
// -> we want/need to know already where the next tile is going to be
spawnPoint.Step(1f);
var roadMaterial = spawnPoint.type switch
{
SpawnPoint.RoadType.LeftTurn => turnLeftMaterial,
SpawnPoint.RoadType.RightTurn => turnRightMaterial,
_ => straightMaterial
};
roadTile.GetComponent<Renderer>().material = roadMaterial;
}
}
}
Behold my Paint skills ;)
This will get you started using Quaternion.Dot.
using UnityEngine;
[RequireComponent(typeof(Renderer))]
public class NewBehaviourScript : MonoBehaviour
{
public Material Material1;
public Material Material2;
public Vector3 Euler = new(90, 0, 0);
private Renderer _renderer;
private void Start()
{
_renderer = GetComponent<Renderer>();
_renderer.material = Material1;
}
private void Update()
{
var dot = Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler));
if (Mathf.Approximately(dot, 1.0f))
{
_renderer.material = Material2;
}
else
{
_renderer.material = Material1;
}
}
}
Using different materials for N, E, S, W corners:
using UnityEngine;
[RequireComponent(typeof(Renderer))]
public class NewBehaviourScript : MonoBehaviour
{
public Material Material1;
public Material Material2;
public Material Material3;
public Material Material4;
public Vector3 Euler1 = new(0, 0, 0);
public Vector3 Euler2 = new(0, 90, 0);
public Vector3 Euler3 = new(0, 180, 0);
public Vector3 Euler4 = new(0, 270, 0);
private Renderer _renderer;
private void Start()
{
_renderer = GetComponent<Renderer>();
_renderer.material = Material1;
}
private void Update()
{
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler1)), 1.0f))
{
_renderer.material = Material1;
}
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler2)), 1.0f))
{
_renderer.material = Material2;
}
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler3)), 1.0f))
{
_renderer.material = Material3;
}
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler4)), 1.0f))
{
_renderer.material = Material4;
}
}
}
Make sure to wrap the rotation past 360 degrees, else it'll always look yellow (4th material).
Hey all I made the following class to act as "bullets" in my game and I don't know how to construct these bullet objects with the params that I want (determined at runtime) and spawn them in the game.
My first attempt looked like this:
if (canShoot())
{
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
but i get the following warnign from the unity editor:
You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all
What is the proper way to spawn these bullets and to set the fields as I want them?
For more info here is the bullet class
public class Bullet : MonoBehaviour
{
private Rigidbody rigidbody;
private float wobble;
private float loft;
private float initialVel;
private Vector2 initialDirectionOffset;
private bool wobbleDirection = false;
private void Awake()
{
this.rigidbody = this.GetComponent<Rigidbody>();
}
private void Start()
{
transform.Rotate(initialDirectionOffset);
rigidbody.AddForce(initialVel*transform.forward);
rigidbody.AddForce(loft*transform.up);
}
private void FixedUpdate()
{
if (wobbleDirection)
{
rigidbody.AddForce(transform.right * wobble);
wobbleDirection = false;
}
else
{
rigidbody.AddForce(-transform.right * wobble);
wobbleDirection = true;
}
}
public Bullet setWobble(float wobble)
{
this.wobble = wobble;
return this;
}
public Bullet setLoft(float loft)
{
this.loft = loft;
return this;
}
public Bullet setInitialVel(float initialVel)
{
this.initialVel = initialVel;
return this;
}
public Bullet setDirectionOffset(Vector2 offset)
{
this.initialDirectionOffset = offset;
return this;
}
}
For more info here is the full "gun" class that spawns the bullets.
public class Gun : MonoBehaviour
{
private int FRAMES_BETWEEN_SHOTS = 10;
private int shotCoolDown = 0;
private const float LOFT = 100F;
private const float WOBBLE = 100F;
private const float INITIAL_VEL = 100F;
private Vector3 directionOffset;
private Recoil internalRecoil;
private void Awake()
{
internalRecoil = this.GetComponent<Recoil>();
if (internalRecoil == null)
{
throw new Exception("Could not find recoil component");
}
}
private void FixedUpdate()
{
if (shotCoolDown > 0)
{
shotCoolDown--;
}
}
private bool canShoot()
{
return shotCoolDown == 0;
}
public void performShoot()
{
if (canShoot())
{
//need to have a recoil component that reacts to bullet fire. then adjusts back to 0,0,0
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
}
}
My second attempt looks like:
GameObject.Instantiate(new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL)
.setDirectionOffset(internalRecoil.getRecoil()));
is this the right way to do it???
As 3Dave mentioned, the best way to do this would be to instantiate a prefab. Assuming you will be dealing with multiple projectiles, I like to separate this into a utility function for instantiating prefabs at a given position.
public static class UnityUtil {
public static GameObject instantiatePrefab(
Object prefab,
Vector3 position,
Transform? parent = null
){
// Create an instance of the prefab
GameObject instance = Object.Instantiate(prefab, position, Quaternion.identity) as GameObject;
// Set the parent
if(parent != null){
instance.transform.parent = parent;
}
return instance;
}
}
Then you could have code like
Transform projectileHolder;
GameObject myBulletPrefab;
Vector3 bulletPosition;
...setup variables...
var BulletGameObj = UnityUtil.instantiatePrefab(myBulletPrefab, bulletPosition, projectileHolder);
Bullet bullet = BulletGameObj.GetComponent<Bullet>();
I keep some other functions in my util for dealing with generic gameobject cases, you can see my full UnityUtil class here
I'm working on some stuff for a simple game engine in xna that I will use for future projects. It's definitely inspired by Unity's framework.
I'm working on a gameobject & component system at the moment and have gotten stuck on cloning a gameobject.
This is how I'm trying to implement it,
public static GameObject CloneGameObject(GameObject obj, Vector2 position, float scale)
{
var clone = new GameObject(); //Create a entirely new gameobject
foreach (var comp in obj.components) //Clone all the components
{
Type t = comp.GetType();
clone.components.Add(Component.CloneComponent<t>()); //This obviously doesn't work
}
clone.position = position;
clone.scale = scale;
AllGameObjects.Add(clone);
return clone;
}
What I'm getting stuck at is how to get the components type to use as a generic argument.
All I'm doing with the component to clone currently is just changing the owner on the clone:
public static TComponent CloneComponent<TComponent>(Component toClone) where TComponent : Component, new()
{
var clone = new TComponent();
clone.gameObject = toClone.gameObject;
return clone;
}
The Component system is simple, all components inherit from the Component class.
Example:
class PlayerController : Component
{
public float jumpForce = 5;
public float walkSpeed = 2;
RigidBody rb;
Collider col;
public override void Start()
{
base.Start();
rb = gameObject.GetComponent<RigidBody>();
col = gameObject.GetComponent<Collider>();
}
bool canJump { get { return jumps > 0; } }
int jumps = 1;
int maxJumps = 1;
public override void Update(GameTime gameTime)
{
rb.velocity = new Vector2(Input.GetAxisInputs(Keyboard.GetState(), "Horizontal") * walkSpeed, rb.velocity.Y);
if (Input.GetKeyOnDown(Keyboard.GetState(), Keys.Space) && canJump)
{
rb.velocity = new Vector2(rb.velocity.X, -jumpForce);
jumps--;
}
if (!col.PlaceIsFree(new Vector2(0, 1)))
jumps = maxJumps;
base.Update(gameTime);
}
}
(Cant get code samples to format correctly so I just used a css snippet ://)
Im not asking for anything about cloning itself, just simply how I can get the correct generic argument or if there is another way to implement this.
You should change a signature of CloneComponent function:
public static TComponent CloneComponent<TComponent>(TComponent toClone) where TComponent : Component, new()
And then you should invoke this function like:
foreach (var comp in obj.components) //Clone all the components
{
clone.components.Add(Component.CloneComponent(comp));
}
You also can use syntactic sugar to simplify invocation code:
public static TComponent Clone<TComponent>(this TComponent toClone) where TComponent : Component, new()
{
if (toClone == null)
{
throw new ArgumentNullException(nameof(toClone));
}
var clone = new TComponent();
clone.gameObject = toClone.gameObject;
return clone;
}
And the loop would be:
foreach (var comp in obj.components) //Clone all the components
{
clone.components.Add(comp.Clone());
}
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.
I'm using XNA to create a Space Invaders copy. So I'm animating many sprites with the same logic placed in their own class, but using different values for most vars. Here is my way of animating from spritesheets:
Texture2D playerTex;
Vector2 playerPos = new Vector2(x, y), playerOrigin;
Rectangle playerHitBox;
float animationTimer = 0f, animationInterval = 100f;
int currentFrame = 1, frameWidth = example number, frameHeight = example number 2;
public void LoadContent(ContentManager Content)
{
playerTex = Content.Load<Texture2D>("ship");
}
public void Update(GameTime gameTime)
{
playerHitBox = new Rectangle(currentFrame * frameWidth, 0, frameWidth, frameHeight);
playerOrigin = new Vector2(playerHitBox.X / 2, playerHitBox.Y / 2);
animationTimer += (float)gameTime.ElapsedGameTime.Milliseconds;
if (animationTimer > animationInterval)
{
currentFrame++;
animationTimer = 0f;
}
if (currentFrame == 2)
{
currentFrame = 0;
}
playerHitBox = new Rectangle(currentFrame * frameWidth, 0, frameWidth, frameHeight);
playerOrigin = new Vector2(playerHitBox.Width / 2, playerHitBox.Height / 2);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(playerTex, playerPos, playerHitBox, Color.White, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0);
}
Instead of using this logic for every animating object within its own class I'm looking for a way to create a sprite class anduse inheritance to Update()/Draw() the sprite. Could something like this be a good approach for the Draw() method?
public void Draw(Texture2D spriteTex, Vector2 spritePos, Nullable<Rectangle> spriteSourceRect, Color spriteColor, Single spriteRotation, Vector2 spriteOrigin, Vector2 spriteScale, SpriteEffects spriteEffects, Single spriteLayerDepth, SpriteBatch spriteBatch)
{
if (spriteTex != null)
{
spriteBatch.Draw(spriteTex, spritePos, spriteSourceRect, spriteColor, spriteRotation, spriteOrigin, spriteScale, spriteEffects, spriteLayerDepth);
}
}
You can:
Create Sprite class to keep common animation properties like texture, duration and current index.
Create Invider class for custom data like position, health and other.
Create a collection to store custom data for each object in game class.
For example:
class Sprite
{
public Texture2D texture;
public Rectangle Frame;
private frameIndex;
private frameCount;
private frameDuration;
private frameInterval;
public Sprite(Texture pTexture, ...)
{
// init sprite data
}
public Update(GameTime pGameTime)
{
// update sprite data
}
}
class Invider
{
private Sprite Sprite;
public Vector2 Porision;
public int Health;
public Invider(Sprite pSprite, Vector2 pPosition)
{
this.Sprite = pSprite;
this.Position = pPosition;
}
public void Update(GameTime pGameTime)
{
// update invider data
}
public void Draw(SpriteBatch pSpriteBatch)
{
pSpriteBatch.Draw(this.Sprite.Texture, this.Sprite.Frame, this.Position, Color.White);
}
}
public class Game1 : Game
{
private SpriteBatch spriteBatch;
private Dictionary<int, Invider> invidersByID;
private Sprite inviderSprite;
public override Initialize()
{
// fill inviderByID collection
}
public override LoadData()
{
// create inviderSprite
}
public static UpdateStatic(GameTime pGameTime)
{
// update static data like frame index
}
public override void Update(GameTime pGameTime)
{
this.inviderSprite.Update(pGameTime);
foreach(Invider invider in invidersByID.Values){
{
invider.Update(pGameTime);
}
}
public override Draw(SpriteBatch pSpriteBatch)
{
this.spriteBatch.Begin();
foreach(Invider invider in invidersByID.Values){
{
invider.Update(pGameTime);
}
this.spriteBatch.End();
}
}