I'm trying to store a series of GameObjects on a List. For some reason any time I put an object on the list, it loses it sprite reference (becomes null). All other object data (position, Color, etc) seems to stay in the object just fine. Here's what I've been trying:
static class Global
{
public static List<GameObject> objects = new List<GameObject>();
}
This is the list I'm using. Now for the object in question - the player:
class Player : GameObject
{
public Vector2 position = Vector2.Zero;
public Texture2D sprite;
public Color image_blend = Color.White;
public Player() : base()
{
//nothing here, nothing in base class either
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(sprite, position, image_blend);
}
}
And finally in my main XNA class (important snippets):
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
sprPlayer = Content.Load<Texture2D>("player");
player = new Player();
player.sprite = sprPlayer;
Global.objects.Add(player);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
for (int i = 0; i < Global.objects.Count; i++)
{
Global.objects[i].Draw(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
I have a feeling I may be going about this the wrong way. Any help is appreciated.
Player has a the Texture2D member called "sprite" not your GameObject class (which you didn't post the code for).
So when you create the Player class, you assign it's member the Texture2D, then you store the Player as it's parent GameObject making all of it's specific object type memebers inaccessible.
To fix, you'll want to make your base GameObject have the member "sprite" and not have on in your Player class that's inheriting from GameObject.
Your list objects is a GameObject list. I am guessing that any data in your player object that is not part of the GameObject class is being thrown out. I'm not sure though..
Also, you really should take a different approach for your sprite's textures. When you decide to add a bunch of entities of players and enemies and what not, each of those entities will have their own separate texture. If you decide to add 100 of these guys or so, you will have 100 duplicates of the same texture.
First I would recommend loading the texture into an array of textures.
//Please excuse my bugs, I haven't used c# in a while
//Place this in your global class:
public static Texture2D[] myTextures = new Texture2D[1]; //or more if you decide to load more.
//And place this in your LoadContent() Method:
Global.myTextures[0] = Content.Load<Texture2D>("player");
//Also, modify your player class:
public int textureIndex = 0;
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Global.myTextures[textureIndex], position, image_blend);
}
Now instead of each player object having it's own separate texture, It uses a texture from the array. Now you can have a lot of entities without unnecessary duplicate textures for each entity
Related
I'm trying to make a game which is spawning an object and after the object is destroyed, another object spawns right away. But right now I'm trying to destroy an instantiate object in a different function, and it is not working.
`
public GameObject[] food;
public Vector3Int spawnPosition;
public void Start()
{
SpawnFood();
}
//Spawning food
public void SpawnFood()
{
int random = Random.Range(0, food.Length);
GameObject clone = (GameObject)Instantiate(food[random], this.spawnPosition, Quaternion.identity);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.C))
{
Destroy(this.gameObject);
}
}
`
I have tried to do some research on this and still, I can only find the solution for destroying an object inside the same function as the Instantiate.
When you call Destroy(this.gameObject), the game object the script attacting to will be destroyed, and after that you cannot call the script.
I guess what you want to destroy is the food game object, not the game object the script you showed here attacting to.
A quick adjustment to suit your need maybe :
...
private GameObject clone
...
private void Update()
{
if(Input.GetKeyDown(KeyCode.C))
{
if (clone != null)
Destroy(clone);
}
}
And as #Daniel seggested, if you will repeatedly instantiate/destroy food game object, the better way would be just use the same food object and change it's properties (e.g. location...) to create the new food game object pop-up illusion.
The key idea here is called Object Pooling.
Here is your answer :
declare a public Gameobject:
public GameObject clone;
and replace
GameObject clone = (GameObject)Instantiate(food[random], this.spawnPosition, Quaternion.identity);
with
GameObject clone = Instantiate(food[random], this.spawnPosition, Quaternion.identity);
and then you can destroy in another function or class
public void DestroyFood()
{
Destroy(clone);
//you can instantiate another gameobject here
}
I'm trying to create a basic Touhou like game as practice for C#, OOP, and Monogame, and part of that is me trying to see if putting all my drawing functions into a seperate class is a good idea.
My concerns have rooted from performance - it seems very redudant to have to create a new GraphicsDeviceManager everytime an object is drawn/created, so I thought I could just have the draw function inherit the GraphicsDeviceManager and possibly even SpriteBatch from the main game class... however this has made my code a bit of a headache for me to organise properly.
Extra context:
So far, I've only really made it so my player can move about, I want to flesh out my classes a lot more before I start putting it all together properly, because otherwise it can become a pain to manage.
So far, my classes are broken down as follows:
Game1.c <- monogame default when created with the crossplatform template
Player.cs <- Handles user input and inherits from the entity class
Entity.cs <- base class for all entities, eg the player, bosses, bullets. Contains speed and rotation data, as well as functions for moving towards a certain point on the screen, as well as some collision handling. (Upon writing this, it seems like it might be best to move the speed and rotation data into GraphicsManager, but I'm not too sure).
GraphicsManager <- Handles setting/changing textures as well as drawing. Entity.cs inherits from here for now, but I think there's a better way of doing this.
Once I fully flesh out the draw class and make sure entity works to my liking I'll start implementing bullets and bullet patterns.
And here are the relevant tidbits of each class:
GraphicsMangager.cs
public class GraphicsManager{
public Texture2D texture;
public Vector2 position;
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
public GraphicsManager(GraphicsDeviceManager g, SpriteBatch sb){
_graphics = g;
_spriteBatch = sb;
}
public void SetTexture(Texture2D newTexture){texture = newTexture);}
pupublic void Draw(){
_spriteBatch.Begin(samplerState: SamplerState.PointClamp);
_spriteBatch.Draw(texture, position, Color.White);
_spriteBatch.End();
}
}
Entity.cs
public class Entity : GraphicsManager {
public float speed;
public float rotation = 0f;
private Vector2 origin, dest;
public int screenWidth, screenHeight;
public Entity(int newscreenWidth, int newscreenHeight, Vector2 newPosition, float newSpeed) {
screenHeight = newscreenHeight;
screenWidth = newscreenWidth;
position = newPosition;
speed = newSpeed;
}
}
Player.cs
public class Player : Entity {
public Player(int newscreenWidth, int newscreenHeight, Vector2 newPosition, float newSpeed) : base(newscreenWidth, newscreenHeight, newPosition, newSpeed) { }
}
If you want to share the same behaviour among other classes, then it is definetely the case of using inheritance, if not then it is case to use composition. Read more about when to choose composition or inheritance in this beatiful answer.
UPDATE:
As your items should be drawn and you have different constructors, then I think it is the case of composition. So in my view, this code structure could be better:
public class Entity
{
public float speed;
public float rotation = 0f;
private GraphicsManager _graphicsManager;
public int screenWidth, screenHeight;
}
I have a script called Weapon.cs, which is a scriptable object.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Weapon : ScriptableObject {
protected Camera _mainCamera;
protected Transform _weaponTransform;
protected int _damage;
protected int _fireRatePerSecond;
protected bool _isAutomaticWeapon;
protected void FireWeapon()
{
//if the weapon is automatic
if (_isAutomaticWeapon)
{
ShootRapidFireOnMouseHold();
}
//if the weapon is semi automatic
else
{
ShootSingleBulletOnMouseClick();
}
}
protected void MoveWeaponWithCamera(Transform weaponTransform)
{
_weaponTransform.rotation = _mainCamera.transform.rotation; //temporary way of making sure the gun moves with the camera
}
protected void ShootSingleBulletOnMouseClick()
{
if (Input.GetKeyDown(KeyCode.Mouse0)) //if left mouse button is clicked
{
CastRay();
}
}
protected void ShootRapidFireOnMouseHold()
{
if (Input.GetKey(KeyCode.Mouse0)) //if left mouse button is held down
{
CastRay(); //rapid fire
//PlayShootAnimation();
}
}
protected void CastRay()
{
RaycastHit hit;
if (Physics.Raycast(_mainCamera.transform.position, _mainCamera.transform.forward, out hit, 100))
{
Debug.Log(hit.transform.name);
}
}
//protected abstract void PlayShootAnimation();
}
I have a second script called MachineGun.cs, which inherits from Weapon.cs, and thus indirectly from scriptable object.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MachineGun : Weapon{
private GameObject _machineGunBarrel;
//private float _animationVelocity;
// Use this for initialization
void Start () {
//initialize the basic properties of a weapon
_damage = 7;
_fireRatePerSecond = 10;
_isAutomaticWeapon = true;
_weaponTransform = GameObject.Find("Weapon_MachineGun").transform;
_machineGunBarrel = GameObject.Find("machinegun_p1");
_mainCamera = Camera.main;
}
// Update is called once per frame
void Update () {
MoveWeaponWithCamera(_weaponTransform);
FireWeapon();
}
//protected override void Shoot()
//{
// RaycastHit hit;
// if(Physics.Raycast(_mainCamera.transform.position, _mainCamera.transform.forward, out hit, 100))
// {
// Debug.Log(hit.transform.name);
// }
//}
void PlayShootAnimation()
{
_machineGunBarrel.transform.RotateAround(_machineGunBarrel.transform.up, _fireRatePerSecond * Time.deltaTime);
PlayShootAnimation(); //play the shoot animation of the gun
}
}
It's currently impossible, since MachineGun.cs doesn't inherit from monobehaviour anymore.
I have a weapon gameobject in my scene, and so here's my question:
How do I go about adding the MachineGun.cs script as a component to my weapon gameobject? Or since this is impossible,
How should I build a weapon system with a general Weapon.cs script from which all weapons can inherit basic functions and fields/variables?
EDIT 1: provided code to my post and added the "why I wanna do this".
Thanks in advance!
ScriptableObjects should mainly be used in a data oriented way, they are very convenient and quite efficient for the task. Also, plaguing your project of MonoBehaviour is a very bad (and wide-spread) practice.
IMO, you should have a MonoBehaviour with weapon logic management and your ScriptableObjects should be your weapon data (which is interpreted by loading them up in your Weapon MonoBehaviour), such that you have Minigun, Glock, Katana.. Scriptable objects which have data like, attack speed, reload speed, charger size, weapon model/textures, reference to model hitbox, yadayadayada. (You might have a generic Weapon MonoBehaviour, but derive a Gun one, a Blade etc.. for very specific management, but which will still need data from ScriptableObjects)
In short your MonoBehaviours define usage and interaction, while your ScriptableObjects define characteristics
ScriptableObject says:
Description
A class you can derive from if you want to create objects that don't
need to be attached to game objects.
So either you should not attach this script to game object or derive your object from "MonoBehaviour" if you want to attach it to game object.
Why did you derive it from ScriptableObject ?
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 5 years ago.
I've been trying to make a test game in XNA (visual studio 2015), but i get a NullReferenceException every time i load this, even though i actually do initialize it. Here is a very short version of it...
namespace Overbox
{
public class Player
{
private Player[] characterList = new Player[14];
virtual public void Initialize()
{
//characterList = new Player[14];
characterList[0] = new Hero();
//for (int i = 0; i <= characterList.Length; i++)
// characterList[i].Initialize();
characterList[0].Initialize();
}
}
virtual public void Draw(SpriteBatch spriteBatch, Texture2D texture)
{
//for (int i = 0; i <= characterList.Length; i++)
//{
// if (characterList[i].Active)
// characterList[i].Draw(spriteBatch, texture);
//}
characterList[0].Draw(spriteBatch, texture); //Here the error occurs
}
}
}
If someone wants the whole class for any reason i could edit this, but this is all the related code i could find.
Edit: stack trace
Overbox.exe!Overbox.Player.Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Microsoft.Xna.Framework.Graphics.Texture2D texture) Line 53 C#
Overbox.exe!Overbox.MediaHandler.Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) Line 54 C#
Overbox.exe!Overbox.Program.Main(string[] args) Line 15 C#
It's all supposed to be there.
Hero class:
namespace Overbox.Characters
{
class Jacketed : Player
{
override public void Initialize()
{
//Unrelated setting of variables
}
public override void Draw(SpriteBatch spriteBatch, Texture2D texture)
{
spriteBatch.Draw(texture, PlayerPosition, Color.White);
}
}
}
Very short version of game loop class:
public class Game1 : Microsoft.XNA.Framework.Game
{
public static Player player;
//I do load the content which playerTexture requires before anyone asks about it
private Texture2D playerTexture;
protected override void Initialize()
{
player = new Player();
player.Initialize();
}
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
player.Draw(spriteBatch, playerTexture);
spriteBatch.End();
}
}
private Player[] characterList = new Player[14];
Makes an array of Player, but does not actually construct the objects. So what you end up with is basically an array full of null values.
You are missing something like this:
public Player()
{
foreach (var character in characterList)
{
character = new Player();
}
}
This is a wild guess, but I'm assuming Hero overrides one or both or Initialize and Draw. Let's say Hero looks like this:
class Hero : Player
{
public override void Initialize()
{
// Do some stuff. Don't call base.Initialize()
}
}
So we've overridden Initialize(), but haven't called base.Initialize(). We've also not overriden Draw(), so the Draw() from Player will be used when called on a Hero instance.
So let's consider when a Player player is constructed. Then player.characterList is also created, but its elements are null. Next, player.Initialize() is called, setting characterList[0] = new Hero(). Note that characterList[0] has its own characterList array as well.
The next thing to happen is characterList[0].Initialize(). This called the Initialize() override in Hero. This does not call Initialize() inside Player, so the characterList inside the Hero instance still has null elements.
Some time in the future, the Draw method is called in the Player. This, in turn, calls characterList[0].Draw(spriteBatch, texture). Since we didn't override the Draw method in Hero, this calls the same method, but on our Hero instance. But remember that the Hero's characterList still has null values? Well, the only thing to happen here is calling characterList[0].Draw(spriteBatch, texture), which as we now know, is calling Draw on a null value.
This is my guess as to what is happening. The details may vary, but I would imagine this sort of thing is the root cause. To be sure, check the stack trace of the exception. You'll notice two frames for the Draw method, letting you know that the exception occurred in a recursive call to Draw.
I've had this solution in mind for a while, but I didn't want to do it because of organizing reasons. All I had to do was to move characterList to the main game loop class and making it public, guaranteeing that it would be initialized before the draw method would be called. This did work.
I am just learning XNA and i have some basic questions.
Let's say I have class Car and want to load/draw sprites. How should I do it? Xna created methods for that but they are in Game class.
After I deal with loading my sprite using custom class my class to have 3 states and depending on state. I want it to draw different sprite. I would like to have them all loaded in array of Texture2d.
As you said, these methods are in the Game class and unless you change radically how things work, you'll have to get used to that fact.
However, as soon as you inherit from DrawableGameComponent (which all your game objects should do if you want them to draw/update), you'll see that all your objects need to have that Game class passed to them in the constructor anyway because of their base class. You can then use it from there to load your textures :
public class Car : DrawableGameComponent
{
private Texture2D texture;
public Car(Game game) : base(game)
{
texture = game.Content.Load<Texture2D>("mytexture");
}
}
Or hold a reference to it and load them later. This also means somewhere in your Game1.cs you have something like
Car car = new Car(this);
or, if you're loading it from an other file, that file need to know your Game class in order to create your car.
To draw the sprites, simply use the
public override void Draw(GameTime gameTime)
method of DrawableGameComponent.
If you want to make it less painful, you could still create a static class that holds your Game class (and possibly your SpriteBatch, as it will be quite useful) :
public static class GameHolder
{
public static Game Game { get; set; }
}
So your game components can be created like this :
public class Car : DrawableGameComponent
{
private Texture2D texture;
public Car() : base(GameHolder.Game)
{
texture = GameHolder.Game.Content.Load<Texture2D>("mytexture");
}
}
And don't forget to feed the Game value from your Game1 class :
public class Game1 : Game
{
public Game1()
{
graphics = new GraphicsDeviceManager(this);
GameHolder.Game = this;
}
}
To handle states, you can indeed load them all in an array and print one of them. To remove the logic from the Draw method, you can also use an other Texture2D that will hold the current sprite you want to render.
public class Car : DrawableGameComponent
{
private List<Texture2D> states;
private Texture2D currentState;
private SpriteBatch spriteBatch;
public Car(Game game, SpriteBatch sb) : base(GameHolder.Game)
{
states.Add(game.Content.Load<Texture2D>("state1"));
states.Add(game.Content.Load<Texture2D>("state2"));
states.Add(game.Content.Load<Texture2D>("state3"));
currentState = states[0];
spriteBatch = sb;
}
public override void Update(GameTime gameTime)
{
if (someCondition)
{
currentState = states[0];
}
else if (someOtherCondition)
{
currentState = states[1];
}
else if (moreConditions)
{
currentState = states[2];
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
spriteBatch.Draw(currentState, new Vector2(0,0), Color.White);
base.Draw(gameTime);
}
}
As you can see, the Draw method don't really care you have multiple states to handle.
If you need a detailed tutorial, the Platformer Starter Kit is really awesome.