XNA Change Game Service dynamically during runtime - c#

I have an interface (ICamera) which is implemented by 2 classes (FreeCamera, StaticCamera). The classes are inheriting from GameComponent.
Example definiton:
public class FreeCamera : GameComponent, ICamera
{
...
}
Now I'm adding the classes to the Game Components and register one of the components to a game service
private FreeCamera freeCam;
private StaticCamera staticCam;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
freeCam = new FreeCamera(this) { Enabled = true };
staticCam = new StaticCamera(this) { Enabled = false };
Services.AddService(typeof(ICamera, freeCam);
Components.Add(freeCam);
Components.Add(staticCam);
...
}
Then I want to change the provider for the service during the application flow with help of a toggle function
namespace Game1
{
protected override void Update(GameTime gameTime)
{
var keyboard = Keyboard.GetState();
if(keyboard.IsKeyDown(Keys.C))
{
if(freeCam.Enabled)
{
Services.RemoveService(typeof(ICamera));
Services.AddService(typeof(ICamera, staticCam);
freeCam.Enabled = !freeCam.Enabled;
staticCam.Enabled = !staticCam.Enabled;
}
else
{
Services.RemoveService(typeof(ICamera));
Services.AddService(typeof(ICamera, freeCam);
freeCam.Enabled = !freeCam.Enabled;
staticCam.Enabled = !staticCam.Enabled;
}
}
base.Update(gameTime);
}
}
The StaticCamera takes only input by mouse (you can rotate the camera), the FreeCamera can also moved by keyboard input. When I call the method above (by pressing C on the keyboard) the FreeCamera class gets deactivated but the viewport seems frozen and does not react to any input. When I call the method again after a short time the FreeCamera gets activated again and everything works as expected.
Now I have 2 questions regarding this:
Is it possible to change the service
provider of a game service in the
game loop?
Is there a better approach
to handle different camera types in a
game and switch between them easily?
Thanks in advance for any help.

Like you answered, use a camera manager. It acts as both a factory and a container for the current camera. The manager you can register as a service. Manager would look something like this:
public class CameraManager
{
private Dictionary<Type, ICamera> _cameras;
private ICamera _current;
public ICamera Current
{
get
{
return _current;
}
}
// Sets the current cammera to the internal instance of the camera type
public void SetCurrent<T>() where T : ICamera
{
if (!_cameras.ContainsKey(typeof(T)))
{
// TODO: Instantiate a new camera class here...
}
_current = _cameras[typeof(T)];
}
}
This is just rough code - would need to be filled in more. One limitation is you can only have one camera per type. Giving cameras a string name, or an enum flag would let you toggle between an arbitrary number of cameras.

Thanks for the tip. I just wrote the code down from my head without my IDE at hand, so please do not look too much into syntax errors etc.
In my game I'm using wrapper classes for the input. The code is just a brief example of the problem - how to substitute a game service if both classes are using the same interface.
My new idea: I could use a "manager" class (like CameraManager in this case) which has the following methods
public void SetCameraType(CameraType type) //CameraType could be an enum
public ICamera GetCamera()
and then put the manager class into the service (with its own interface like ICameraManager).
Edit: this was considered as an answer to the comment above ... but it seems I clicked the wrong button - sorry

I think if you left off the code about adding and removing the service in the Update you'd be good and added lines changing the Visible property. The Enable property effects calls to Update, but the Visible property effects calls to Draw.
So I'd suggest the Update look like this:
protected override void Update(GameTime gameTime)
{
var keyboard = Keyboard.GetState();
if(keyboard.IsKeyDown(Keys.C))
{
if(freeCam.Enabled)
{
freeCam.Enabled = false;
freeCam.Visible = false;
staticCam.Enabled = true;
staticCam.Visible= true;
}
else
{
freeCam.Enabled = true;
freeCam.Visible = true;
staticCam.Enabled = false;
staticCam.Visible= false;
}
}
base.Update(gameTime);
}

Related

Variable not being set in class using inheritance

Edit: It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!
I have an issue with the class below called ProjectileWeapon. It is based on an abstract class called Weapon, and that class inherits MonoBehaviour.
Weapon has two abstract functions called BeginCycle and EndCycle which are implemented in the ProjectileWeapon class. Those functions set a variable called "firing".
The problem is, "firing" doesn't ever seem to be set despite the functions being called correctly. I know the functions are called because I can see the prints in the console.
Also, when I use that variable in the update function, it doesn't do anything because the variable never changes.
The OnGUI function is working and is displaying text on screen, however the "firing" variable is never updated.
Am I misunderstanding how to use inheritance?
This class is on the weapon prefab which is then instantiated during the equipping function in the game
public class ProjectileWeapon : Weapon
{
private bool firing;
private float firingTimer;
void Start()
{
print("ProjectileWeapon start");
}
void OnGUI()
{
GUI.Label(new Rect(0,100,100,100), "ProjectileWeapon firing: " + firing);
}
void Update()
{
// this function is called but "firing" is not updated
}
public override void BeginCycle()
{
print("projectile begin cycle");
firing = true;
}
public override void EndCycle()
{
print("projectile end cycle");
firing = false;
}
}
Here's the base class:
public abstract class Weapon : MonoBehaviour
{
public abstract void BeginCycle();
public abstract void EndCycle();
}
EDIT: Here is the code that calls the above
This component is added to the player game object
public class WeaponHandler : MonoBehaviour
{
public bool FireInput { get; set; } // set to true when user holds the mouse button down, and false when let go
public Weapon WeaponBehaviour; // this is the script that does the weapon functionalility. Any subclass of Weapon can be put here e.g. ProjectileWeapon, MeleeWeapon
private bool isFiring = false;
void OnGUI()
{
GUI.Label(new Rect(0,0,100,100), "fire input:" + FireInput + ", isFiring:" + isFiring);
}
void Update()
{
// fire weapon
if (WeaponBehaviour && FireInput && !isFiring)
{
ActivateWeapon();
}
else if (!FireInput && isFiring)
{
DeActivateWeapon();
}
}
private void ActivateWeapon()
{
print("activate weapon");
isFiring = true;
WeaponBehaviour.BeginCycle();
}
private void DeActivateWeapon()
{
print("deactivate weapon");
isFiring = false;
WeaponBehaviour.EndCycle();
}
}
Based on the code you've provided, there are three possible scenarios:
You're not calling the functions. However, if the print functions are called, then you must be calling them.
You're calling both functions, which sets the variable to true, and then to false.
You're overriding the variable with a local variable with the same name. Visual Studio will warn you if that's the case.
It's hard to tell without knowing where the variables are supposed to be called. If you upload the rest of the code, I'm sure the answer will be clear.
It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!

Assign a method to a instantiated sprite

I'm creating a 2d Turret Defense game in C# and I have a question about the management of the updates of the turrets.
I want to create the menu over the turret sprite with several options for the upgrade.
My idea is to load the menu from the resources as sprite, assign it as child to the turret gameObject and then manage the comunication between the button to the turret to start the upgrade process.
There is a way to assign a method/function to a resource freshly loaded?
For example (in turret class):
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
myMenuButton.OnMouseDown(){
// do something with the current turret's class
};
Or I have to save the gameObject sprite in resource folder with inside a script that will manage the OnMouseDown method searching the parent script of the turret and use the correct method?
For example (in menu sprite class):
void OnMouseDown(){
GameObject parent = gameObject.transform.parent.gameObject;
turretScript parentScript = (turretScript) go.GetComponent(typeof(turretScript));
parentScript.doUpgrade();
}
Thanks for any help!
I don't know about Sprite itself (documentation may have information about it, did you already research there?)
But I don't see why you cant use Buttons instead of Sprite to handle this "menu behaviour" you wants to do. Button class have an onClick event handler that you can use to assign listners at runtime like
myMenuButton.onClick.AddListener(() => {
//handle click here
});
you can also set methods by lambda expression, just google about it and you'll find a lot of resources to learn how to use it ;)
first create a class which already implements the OnMouseDown and a list of function pointers and attach this class to every single item you are going to use it on beforehand.
public class MouseDownScript : MonoBehaviour
{
public List<Action> Actions = new List<Action>();
void OnMouseDown(){
foreach(Action action in Actions)
action();
}
}
Then you have the ability to:
{
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
// Cache the script
MouseDownScript mouseScript = myMenuButton.GetComponent<MouseDownScript>();
// Add anonymous actions with a lambda as such:
mouseScript.Actions.Add(() => /*Any action here*/);
// Add existing functions as such:
mouseScript.Actions.Add(MouseDownAction));
}
public void MouseDownAction() {}
I don't know Unity but you might be able to inherit from Sprite, add a property to it of type Action and assign that after you create it with Resources.Load
class Program
{
static void Main(string[] args)
{
var s = new Sprite();
s.Action = dostuff;
// outputs Hello
s.Action.Invoke();
Console.ReadLine();
}
public static void dostuff() { Console.WriteLine("Hello"); }
public class Sprite
{
public Action Action { get; set; }
}
}
You can add new class to Sprite when create Sprite
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
myMenuButton.AddComponent<ClassForSprite>();
In ClassForSprite
public class MouseDownScript : MonoBehaviour
{
void onAction1(){
//Code process for action 1
}
void onAction2(){
//Code process for action 2
}
}
I think you'll want to use delegates.
Basically you define a method signature e.g.
public delegate void ClickHandler();
then you define an instance of that signature:
public ClickHandler OnClick;
you can assign a method to OnClick on runtime and execute.
in turret:
public void ExecuteOnTurrent()
{
// do stuff in turret
}
public void LoadSprite()
{
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
myMenuButton.OnClick= ExecuteOnTurrent;
}
in sprite
public delegate void ClickHandler();
public ClickHandler OnClick;
public void Update()
{
//- detect click or other action
if(click and OnClick!= null)
{
OnClick();
}
}

System.NullReferenceException when trying to pass an Object

For practice we have to make Tetris and we were given a template project to work with in Visual Studio (with XNA). Now that we got the gist of it down, I'm trying to introduce sounds. However, soundengine is declared is within the GameWorld class, but the actual gameplay events trigger in TetrisGrid, again a separate class.
Now one solution would be to move the entire initialization of it to the TetrisGrid object, but we sometimes want to play sounds in GameWorld too, for instance in a menu screen. Also having the music controls there would be a lot more practical, since the TetrisGrid may be reset after losing/winning the game.
The soundengine spawns from here:
namespace tetrisXNA
{
class SfxHelper
{
private SoundEffect[] sfxLibrary;
public SfxHelper(ContentManager Content)
{
this.sfxLibrary = new SoundEffect[2];
this.sfxLibrary[0] = Content.Load<SoundEffect>("pop");
this.sfxLibrary[1] = Content.Load<SoundEffect>("latch");
}
public void popRow()
{
this.sfxLibrary[0].Play();
}
public void latchBlock()
{
this.sfxLibrary[1].Play();
}
}
}
Which works just fine when using it in the object you initialised it, but trying to pass it to the tetrisgrid object (both using ref and normally). All classes are within the same namespace.
public GameWorld(int width, int height, ContentManager Content)
{
[...]
this.soundengine = new SfxHelper(Content);
}
Constructor where the soundengine is assigned (in gameworld).
The tetrisGrid object constructor:
public TetrisGrid(Texture2D texture, SfxHelper soundEngine)
{
...
this.random = new Random(System.DateTime.Now.Millisecond);
this.soundengine = soundEngine;
...
}
The soundengine is declared above as public SfxHelper soundengine;. So how do I make the same instance of it accessible to different objects/layers, how to pass it correctly? I've tried looking into the static modifier but I've yet to implement that correctly, because it does suit the job as only one sound engine is really required.

Accessing information between scenes in Unity

I have a class in Unity that a list of toggle switches that get turned on and off in a separate scene from the rest of my game. What I'm wanting is to have the user select one button and then have a corresponding action happen in my main game when they go back to that scene. However, I'm having issue sending information between scenes.
At the moment my toggle class looks like this:
private bool action1 = false;
public bool Action1
{
get { return action1;}
}
void OnGUI()
{
action1 = GUI.Toggle(new Rect(10, 10, 100, 30), action1, "test");
}
void Update()
{
if(Input.GetButton("Jump"))
{
Application.LoadLevel("Main");
}
}
Then in a class held in my Main scene, I have the following code:
private ActionClass actionIsOn = new ActionClass();
void Start()
{
if(actionIsOn.Action1 == true)
{
Debug.Log("action is on");
}
else
{
Debug.Log("nothing happening");
}
}
However, when I test this, nothing happens.
Have I set this up correctly? Is there a better way to pass this information from one scene to another?
Option #1:
Use a static class to hold global info that's relevant to multiple scenes.
public static class GlobalData
{
public static bool SomeBooleanFlag;
}
This way in your first scene you can set GlobalData.SomeBooleanFlag to some value, and in your second scene you can check for it.
Option #2:
You can use Object.DontDestroyOnLoad to ensure that an object in your scene doesn't get destroyed when a new scene is loading. This way you can aggregate all of the info that you want to pass to the other scene in a single object (or use multiple objects, and keep them all alive), and make sure it stays alive even after the scene has changed.
http://docs.unity3d.com/Documentation/ScriptReference/Object.DontDestroyOnLoad.html

Displaying Different Screens in XNA?

Recently started working with XNA (coming from java) and run into a problem with displaying game screens. When loading up XNA I get given a game.cs class which I interpreted to be a set a functions for drawing a single self-contained screen in the game. As obviously typing the code for all your different screens into this single class would get very messy very quickly so I created the below class to handle changes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Colonies
{
public class GameManager //manages game screens
{
Microsoft.Xna.Framework.Game currentScreen;
public enum screens { menu, game, summary };
public GameManager()
{
initialize();
}
public void initialize()
{
currentScreen = new Menu(this);
((Menu)currentScreen).Run();
}
public void changeScreen(int i)
{
switch (i)
{
case 0:
currentScreen = new Menu(this);
((Menu)currentScreen).Run();
break;
case 1:
currentScreen = new World(this);
((World)currentScreen).Run();
break;
case 2:
currentScreen = new Summary(this);
((Summary)currentScreen).Run();
break;
}
}
}
}
However when one of these changes is triggered this causes an error flag up telling me I can't call game run more than once. Does this mean by initial estimation about having a single all purpose game screen is actually correct?! Should be manager instead be being queried for game like screens which methods are then called in the main game.cs class?
i.e.
in game.cs update method for example:
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
aGameManager.getAGameObject.DoAnUpdate();
base.Update(gameTime);
}
So essentially my main game class is never run again but just changes what it displays. Would this be the correct solution? (So much of the game class is hidden I am not sure what is the correct way to use it)
The Game class is the entire game. That's why it's called Game. If you want, you can create 'screen' objects, that each control a different screen, and use the Game class as you were trying to use the 'game manager'.
EG:
public static int currentScreen = 0; // Any screen can change this variable when needed
List<Screenobject> myscreens = new List<Screenobject>(); // Populate this with screens
// OR
menuscreen = new menuScreen();
otherscreen = new otherScreen();
// ...
protected override void Update(GameTime gameTime)
{
myscreens[currentScreen].Update(gameTime);
// OR
switch (currentScreen)
{
case 1:
menuscreen.Update(gameTime); break;
// ...
}
base.Update(gameTime);
}
and Draw(..) the same as Update(..)
create enumerator
enum gamestate
mainmenu
gameplay
options
end enum
and then simply in your update (draw) main functions
if gamestate = mainmenu then mainmenu.update();
if gamestate = gameplay then gameplay.update()

Categories

Resources