I recently learned about design patterns and wanted to change my code for a small game I had to make. The game is called SpaceTaxi. I've made a parser, parsing a .txt file with ascii content resulting in 4 different lists of entities: taxi, obstacle, exit and platform. Inside the gameloop I call a big collision-method using these entities detecting whether they collide. It's really ugly code. Is there a good design pattern I could use for my collision method? So instead of having a big method then have smaller classes? Currently it looks like this:
/// <summary>
/// Checks for collision with obstacles, platforms and exits.
/// </summary>
private void CollisonCheck() {
var platformCollision = Player.CollisionPlatform(Parser.PlatformEntities,
false);
var obstacleCollision = Player.CollisionObstacle(Parser.ObstacleEntities,
false);
var exitCollision = Player.CollisionObstacle(Parser.ExitEntities,
false);
// Landing on platform
if (platformCollision.Item1 && obstacleCollision) {
// Stand still on platform
if (Math.Abs(Player.Entity.Shape.AsDynamicShape().Direction.Y)
< Constants.COLLISION_DISTANCE) {
Player.Shape.Direction.Y = 0;
Player.Shape.Direction.X = 0;
Player.OnPlatform = true;
// Explode because of too much speed
} else {
AddExplosion(Player.Shape.Position.X, Player.Shape.Position.Y,
0.1f, 0.1f);
}
// Be rewarded in case player transports a customer
if (Player.HasCostumer) {
foreach (var customer in pickedUpCustomers) {
if (CorrectDestination(platformCollision.Item2,
customer.DestinationPlatform)) {
score.AddPoint(CurrentCustomer.RewardPoints);
customer.CanRemove = true;
Player.HasCostumer = false;
}
}
}
// Exit map
} else if (exitCollision) {
// Switch from one map to another
if (GameRunning.CurrentMap == "the-beach.txt") {
GameRunning.CurrentMap = "short-n-sweet.txt";
Player.SetPosition(Constants.PLAYER_ENTRYPOSITION_X,
Constants.PLAYER_ENTRYPOSITION_Y);
Player.Entity.Shape.AsDynamicShape().Direction.Y = Constants.STILL;
Player.Entity.Shape.AsDynamicShape().Direction.X = Constants.STILL;
// Switch from one map to another
} else {
GameRunning.CurrentMap = "the-beach.txt";
Player.SetPosition(Constants.PLAYER_ENTRYPOSITION_X,
Constants.PLAYER_ENTRYPOSITION_Y);
Player.Entity.Shape.AsDynamicShape().Direction.Y = Constants.STILL;
Player.Entity.Shape.AsDynamicShape().Direction.X = Constants.STILL;
}
GameRunning.Timer.Restart();
Parser.Load(GameRunning.CurrentMap);
allCustomersInMap = new List<Customer>();
foreach (var c in Parser.Customer) {
allCustomersInMap.Add(new Customer(c.Key, c.Value.Item1,
c.Value.Item2, c.Value.Item3, c.Value.Item4,
c.Value.Item5));
}
// Collision with obstacle. Add explosion
} else if (obstacleCollision) {
AddExplosion(Player.Shape.Position.X, Player.Shape.Position.Y,
Constants.EXPLOSION_WIDTH, Constants.EXPLOSION_HEIGHT);
TaxiBus.GetBus()
.RegisterEvent(GameEventFactory<object>.CreateGameEventForAllProcessors(
GameEventType.GameStateEvent, this, "CHANGE_STATE",
"MAIN_MENU", ""));
}
// Collision with taxi and customer
// CollisionCustomer returns a bool (item1) and null/customer (item2)
if (Player.CollisionCustomer(allCustomersInMap).Item1 && !Player.HasCostumer) {
var customer = Player.CollisionCustomer(allCustomersInMap).Item2;
TaxiMeterTimer = new Stopwatch();
TaxiMeterTimer.Start();
CurrentCustomer = customer;
pickedUpCustomers.Add(customer);
allCustomersInMap.Remove(customer);
CurrentCustomer.SetPosition(Constants.HIDEPOS_X, Constants.HIDEPOS_Y);
Player.HasCostumer = true;
}
}
Please note that questions like this are better suited for CodeReview as this is pretty much off topic for SO because you do not have a specific problem or exception that you are trying to resolve.
It is good practise in all applications to separate out decision logic from action logic, for a number of reasons:
Removing the implementation of actions from complex logic trees makes it easier to visualise the decision process, you assume that the actions work correctly and debug them separately
Creating single purpose action methods promotes code re-use from different decision logic trees in your program
You can easily test and debug individual actions or logic branches without disrupting or being distracted by the bigger picture.
You can more clearly document your intent with method Doc Comments
Just separating out the actions you have defined results in code similar to this:
/// <summary>
/// Checks for collision with obstacles, platforms and exits.
/// </summary>
private void CollisonCheck()
{
var platformCollision = Player.CollisionPlatform(Parser.PlatformEntities, false);
var obstacleCollision = Player.CollisionObstacle(Parser.ObstacleEntities, false);
var exitCollision = Player.CollisionObstacle(Parser.ExitEntities, false);
// Landing on platform
if (platformCollision.Item1 && obstacleCollision)
DockAtPlatform(); // Stand still on platform
else if (exitCollision)
ExitMap(); // Exit map
else if (obstacleCollision)
CollideWithObject(); // Collision with obstacle. Add explosion
// ??? Player collision can occur at a platform or in general regions in the map
if (Player.CollisionCustomer(allCustomersInMap).Item1 && !Player.HasCostumer)
PickupCustomer();
}
/// <summary>
/// Dock Player with platform as long as they are not approaching too fast.
/// Collect reward if carrying a passenger
/// </summary>
/// <remarks>If too fast, player will explode!</remarks>
private void DockAtPlatform()
{
if (Math.Abs(Player.Entity.Shape.AsDynamicShape().Direction.Y)
< Constants.COLLISION_DISTANCE)
{
Player.Shape.Direction.Y = 0;
Player.Shape.Direction.X = 0;
Player.OnPlatform = true;
// Explode because of too much speed
}
else
{
AddExplosion(Player.Shape.Position.X, Player.Shape.Position.Y,
0.1f, 0.1f);
}
// Be rewarded in case player transports a customer
if (Player.HasCostumer)
{
foreach (var customer in pickedUpCustomers)
{
if (CorrectDestination(platformCollision.Item2,
customer.DestinationPlatform))
{
score.AddPoint(CurrentCustomer.RewardPoints);
customer.CanRemove = true;
Player.HasCostumer = false;
}
}
}
}
/// <summary>
/// Switch between Maps
/// </summary>
private void ExitMap()
{
// Switch from one map to another
if (GameRunning.CurrentMap == "the-beach.txt")
{
GameRunning.CurrentMap = "short-n-sweet.txt";
Player.SetPosition(Constants.PLAYER_ENTRYPOSITION_X,
Constants.PLAYER_ENTRYPOSITION_Y);
Player.Entity.Shape.AsDynamicShape().Direction.Y = Constants.STILL;
Player.Entity.Shape.AsDynamicShape().Direction.X = Constants.STILL;
}
else
{
// Switch the reverse way around
GameRunning.CurrentMap = "the-beach.txt";
Player.SetPosition(Constants.PLAYER_ENTRYPOSITION_X,
Constants.PLAYER_ENTRYPOSITION_Y);
Player.Entity.Shape.AsDynamicShape().Direction.Y = Constants.STILL;
Player.Entity.Shape.AsDynamicShape().Direction.X = Constants.STILL;
}
GameRunning.Timer.Restart();
Parser.Load(GameRunning.CurrentMap);
allCustomersInMap = new List<Customer>();
foreach (var c in Parser.Customer)
{
allCustomersInMap.Add(new Customer(c.Key, c.Value.Item1,
c.Value.Item2, c.Value.Item3, c.Value.Item4,
c.Value.Item5));
}
}
/// <summary>
/// Show explosion because player has collided with an object, then return to the main menu
/// </summary>
private void CollideWithObject()
{
AddExplosion(Player.Shape.Position.X, Player.Shape.Position.Y,
Constants.EXPLOSION_WIDTH, Constants.EXPLOSION_HEIGHT);
TaxiBus.GetBus()
.RegisterEvent(GameEventFactory<object>.CreateGameEventForAllProcessors(
GameEventType.GameStateEvent, this, "CHANGE_STATE",
"MAIN_MENU", ""));
}
/// <summary>
/// Pickup a new customer, start the meter running and remove the customer from the map
/// </summary>
private void PickupCustomer()
{
var customer = Player.CollisionCustomer(allCustomersInMap).Item2;
TaxiMeterTimer = new Stopwatch();
TaxiMeterTimer.Start();
CurrentCustomer = customer;
pickedUpCustomers.Add(customer);
allCustomersInMap.Remove(customer);
CurrentCustomer.SetPosition(Constants.HIDEPOS_X, Constants.HIDEPOS_Y);
Player.HasCostumer = true;
}
Now you can focus on the individual actions and review them to see if there are cleaner ways to code their process.
A few notes to consider:
Add Explosion should accept a point object instead of passing through the X,Y coordinates, it makes the code easier to read and is a natural way to pass coordinates that always go together.
Instead of using Tuples, you should create specific data model classes to use as return values from the collision functions, it feels like over-engineering, but it helps both with documentation of your code and to understand the intent.
Takes little time to define a class to hold the collision response, with minimal documentation, instead of burying inside comments what .Item1 and .Item2 might be in terms of data type and logical meaning
Yes Tuples are awesome, in terms of rapidly smashing code solutions out, but you always have to review the code that creates the values to understand or identify the meaning behind the values, so these are usually the first to be refactored out of my prototyping code when I want to get serious about a solution.
You have a style of placing the comments for the next if block inside the previous branch code, move the comments to inside the affected if branch, or immediately above it
Or move the branch into its own method and you can use Doc comments.
If the Player object itself holds the meter variables, you could accept multiple passengers (up to the capacity of the vehicle) and collect multiple fares on delivery, your action for dropping off a customer at a platform already assumes multiple passengers.
You may have already catered for this in different ways
Final Note (a personal one)
Try to change your method logic into more cause and effect style, you gain a lot of benefits in the overall SDLC if you adopt more functional programming styles into your OO logic. Where you can, and especially for the smallest units of work, pass in the objects that will be operated on as arguments for your methods rather than referencing global objects, in this way you will find it easier to establish unit testing and code review of your individual methods.
Even though in this project you may not implement unit tests, it is a very good habit to evolve, it will make future code examples that you post on SO easier for the rest of us to debug and will allow you to be a more efficient contributor in a team or community development project.
Related
I have a function for raycasting on a grid with Bresenham's algorithm. I want to optimize for specific cases when the line is orthogonal or diagonal; of course, this logic won't be returned/exposed to the user.
Is it possible to detect the optimization edge case throught a unit test? E.g. Look for a specific Debug.WriteLine marker when calling the function.
I'm new to unit testing and I might be missing the point (testing functionality exposed to the user) but asserting that my edge cases work as expected can be invaluable - especially when actively developing the optimizations.
Example of what I want to achieve:
public IEnumerable<Coordinate> RayCast (Coordinate source, Coordinate direction) {
if (direction.X == 0 || direction.Y == 0) {
Debug.WriteLine ("Orthogonal_Edge_Case");
//Simple iteration across 1 axis
...
yield break;
}
if (Math.Abs(direction.X) == Math.Abs(direction.Y)) {
Debug.WriteLine ("Diagonal_Edge_Case");
//Simple diagonal iteration
...
yield break;
}
//Standard Bresenham's algorithm
...
yield break;
}
...
[TestMethod]
public void TestRayCast () {
var source = new Coordinate (0,0);
var orthogonal = new Coordinate (0,1);
CoordinateUtil.RayCast (source, orthogonal);
//Check that the Orthogonal_Edge_Case marker was emitted
var diagonal = new Coordinate (1,1);
CoordinateUtil.RayCast (source, diagonal);
//Check that the Diagonal_Edge_Case marker was emitted
//Usual tests for RayCast
...
}
Note: I'm using Visual Studio 2019's unit test kit but I'm curious if this is possible with any .NET tool
You have a couple of options:
Hold some sort of state using a property in CoordinateUtil which will allow you to check what was the last edge detected. This will however break the Command Query Responsibility Segregation pattern.
Inject an edge case detector (which you can easily mock with mocking framework). The example below use Moq.
Inject an ILogger which will allow you to log some timing information amongst other things (very similar to option2 but with slightly different set up).
Below shows how option 2 works. You may feel it is overkill but it is robust and how things are generally done in large projects. Also note that you are now exposing internals to the outside world, just to help with internal optimisation. If so then this may not be appropriate.
public class CoordinateUtil
{
private readonly IEdgeCaseDetector edgeCaseDetector;
// This is the important bit where you inject an edge case detector
public CoordinateUtil(IEdgeCaseDetector edgeCaseDetector)
{
this.edgeCaseDetector = edgeCaseDetector;
}
public IEnumerable<Coordinate> RayCast(Coordinate source, Coordinate direction)
{
if (direction.X == 0 || direction.Y == 0)
{
edgeCaseDetector.Detect("Orthogonal_Edge_Case");
//Simple iteration across 1 axis
yield break;
}
if (Math.Abs(direction.X) == Math.Abs(direction.Y))
{
edgeCaseDetector.Detect("Diagonal_Edge_Case");
//Simple diagonal iteration
yield break;
}
//Standard Bresenham's algorithm
yield break;
}
}
public interface IEdgeCaseDetector
{
void Detect(string message);
}
public class EdgeCaseDetector
{
public void Detect(string message)
{
// If you wanted to you could simply save the edge cases to a public property here
// Or you might want to log them when you code runs outside of the unit test
}
}
[TestClass]
public class CoordinateUtilTests
{
[TestMethod]
public void RayCast_WhenOthogonal_DetectsEdgeCase()
{
// Arrange
var mock = new Mock<IEdgeCaseDetector>();
var coordinateUtil = new CoordinateUtil(mock.Object);
var source = new Coordinate(1, 1);
// Act
// Remember the ToArray because we need to evaluate the enumerable
// before we can check if the edge case was detected.
coordinateUtil.RayCast(source, new Coordinate(0, 0)).ToArray();
// Assert
mock.Verify(x => x.EdgeDetected("Orthogonal_Edge_Case"));
}
}
Exposing internal logic to the external world only for sake of the tests, wouldn't be best approach.
You should test only against public api and behaviour.
For performance tests it can be tricky, but doable. For example what behaviour consumer of the method under the test will observe if you execute Standard Bresenham's algorithm for edge cases?
Does execution will takes more then 500 milliseconds? If so, write test for it and check that for edge cases execution takes less then 500 milliseconds.
If you can not find such observable behaviour I would not write tests for it, and just trust developer who ever write or maintain this code. All developers smart people and try their best.
I needed to find inactive objects in Unity3D using C#.
I have 64 objects, and whenever I click a button then it activates / inactivates objects for the corresponding button at runtime. How can I find inactive objects at this time?
Since Unity 2020
In the years since this question was asked, Unity put in the exact thing you need. At least, the exact thing I needed. Posting here for future peoples.
To find an object of a certain type whether it's on an active or inactive GameObject, you can use FindObjectsOfType<T>(true)
Objects attached to inactive GameObjects are only included if inactiveObjects is set to true.
Therefore, just use it like you regularly would, but also pass in true.
The following code requires System.Linq:
SpriteRenderer[] onlyActive = GameObject.FindObjectsOfType<SpriteRenderer>();
SpriteRenderer[] activeAndInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true);
// requires "using System.Linq;"
SpriteRenderer[] onlyInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true).Where(sr => !sr.gameObject.activeInHierarchy).ToArray();
The first array includes only SpriteRenderers on active GameObjects, the second includes both those on active and inactive GameObjects, and the third uses System.Linq to only include those on inactive GameObjects.
See this answers for Unity 2020 and higher.
Before Unity 2020
Well, using GameObject.Find(...) will never return any inactive objects. As the documentation states:
This function only returns active gameobjects.
Even if you could, you'd want to keep these costly calls to a minimum.
There are "tricks" to finding inactive GameObjects, such as using a Resources.FindObjectsOfTypeAll(Type type) call (though that should be used with extreme caution).
But your best bet is writing your own management code. This can be a simple class holding a list of objects that you might want to find and use at some point. You can put your object into it on first load. Or perhaps add/remove them on becoming active or inactive. Whatever your particular scenario needs.
If you have parent object (just empty object that plays role of a folder) you can find active and inactive objects like this:
this.playButton = MainMenuItems.transform.Find("PlayButton").gameObject;
MainMenuItems - is your parent object.
Please note that Find() is slow method, so consider using references to objects or organize Dictionary collections with gameobjects you need access very often
Good luck!
For newer Unity versions this answer provides probably a better solution!
First of all
In general any usage of Find or it's variants should be avoided.
Actually they are never really required but only a "hot-fix" used to cover an implementation "laziness".
Usually from the beginning storing and passing on required references is always the better approach.
Especially in your case you seem to have a fix amount of objects so you could probably already reference them all in a certain "manager" component and store them in a list or array (them you can get a reference by index) or even a Dictionary<string, GameObject> (then you can also get the according reference by name - you can find an example below).
Workarounds
There are alternative solutions (FindObjectsWithTag, FindObjectsOfType) but it will always be quite expensive (though most of the Find variants are expensive anyway).
You could e.g. also "manually" iterate through all objects in the scene using Scene.GetRootGameObjects
Returns all the root game objects in the Scene.
And then search through them until you find your object. This way you get also inactive GameObject.
public static GameObject Find(string search)
{
var scene = SceneManager.GetActiveScene();
var sceneRoots = scene.GetRootGameObjects();
GameObject result = null;
foreach(var root in sceneRoots)
{
if(root.name.Equals(search)) return root;
result = FindRecursive(root, search);
if(result) break;
}
return result;
}
private static GameObject FindRecursive(GameObject obj, string search)
{
GameObject result = null;
foreach(Transform child in obj.transform)
{
if(child.name.Equals(search)) return child.gameObject;
result = FindRecursive (child.gameObject, search);
if(result) break;
}
return result;
}
But ofcourse this should be strongly avoided and the usage of such deep searches reduced to a minimum!
What I would do
Another way - in my eyes the best approach here - could be to have a certain component attached to all your objects and actually store all the references once as said before in a dictionary like e.g.
public class FindAble : MonoBehaviour
{
private static readonly Dictionary<string, GameObject> _findAbles = new Dictionary<string, GameObject>();
public static GameObject Find(string search)
{
if(!_findAbles.ContainsKey(search)) return null;
return _findAbles[search];
}
private IEnumerator Start()
{
// Wait one frame
// This makes it possible to spawn this object and
// assign it a different name before it registers
// itself in the dictionary
yield return null;
if(_findAbles.ContainsKey(name))
{
Debug.LogError($"Another object with name /"{name}/" is already registered!", this);
yield break;
}
_findAbles.Add(name, gameObject);
}
private void OnDestroy ()
{
if(_findAbles.ContainsKey(name))
{
_findAbles.Remove(name);
}
// Optionally clean up and remove entries that are invalid
_findAbles = _findAbles.Where(kvp => kvp.Value).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
and then use it like
var obj = FindAble.Find("SomeName");
if(obj)
{
// ...
}
Also for this the component would need to be enabled at least once so Start is called.
Again an alternative would be to have instead a
public void Initialize(string newName)
{
if(_findAbles.ContainsKey(name))
{
Debug.LogError($"Another object with name /"{name}/" is already registered!", this);
return;
}
name = newName;
_findAbles.Add(name, gameObject);
}
which you could call also after e.g. spawning an inactive object.
You can use Predicates.
Just get the gameObjects and check them whith a Predicate as below:
public List<GameObject> FindInactiveGameObjects()
{
GameObject[] all = GameObject.FindObjectsOfType<GameObject> ();//Get all of them in the scene
List<GameObject> objs = new List<GameObject> ();
foreach(GameObject obj in all) //Create a list
{
objs.Add(obj);
}
Predicate inactiveFinder = new Predicate((GameObject go) => {return !go.activeInHierarchy;});//Create the Finder
List<GameObject> results = objs.FindAll (inactiveFinder);//And find inactive ones
return results;
}
and don't forget using System; using System.Collections.Generic;
You can do this at runtime by having your inactive gameobject under an active parent object as previously mentioned; slightly different from what was mentioned, this is an approach I've used for activating/deactivating menus that should be inactive by default:
canvas = GameObject.FindGameObjectWithTag("GameMenu").GetComponentInChildren<Canvas>().gameObject;
Now you can change its activeSelf to toggle it in a method/event listener of your choice:
canvas.SetActive(!canvas.activeSelf);
Even while it is inactive, you can still use the tag property of it and use it for a filter, if getting multiple components of the same type. I haven't tested this using GetComponentsInChildren, but you could probably use a 'Single' linq query, and get the object by tag name which would require creating a tag for every gameobject you want to do this to.
Although its not the correct answer, but this is what I did in my case.
1) Attach a script to (inactive) game objects and instead of setting then inactive keep it active.
2) Position them out of the scene somewhere.
3) Set a flag in the script which says inactive.
4) In Update() check for this inactive flag and skip function calls if false.
5) When needed the object, position it at the proper place and set the flag active.
It will be a bit of a performance issue but that's the only workaround I could think of so far.
We're creating a game for a school project. It's a 2D platformer and it is in its very early stages. We use C#/XNA and we're implementing Farseer Physics Engine.
I'm currently struggling with the map-class. In the class we have a List of DrawableGameObjects, were we store each tile of the map and draw them. But when we try to draw them we get a "Assertion Failed". Examining the problem even further I've come to the conclusion that whenever we try to add more than to static bodies to the world (even without drawing them) we get this failure. Throw message
Game1.cs:line 210 is:
world.Step(0.033333f);
And Program.cs:line 15 is:
game.Run();
Here is the code for the Map class:
class Map
{
private List<DrawableGameObject> ground = new List<DrawableGameObject>();
public Map(World world, Texture2D texture)
{
for (int i = 0; i < 32; i++)
{
DrawableGameObject floor = new DrawableGameObject(world, texture, new Vector2(40, 40), 100, "ground");
floor.Position = new Vector2(i * 40, 500);
floor.body.BodyType = BodyType.Static;
ground.Add(floor);
}
}
public void Draw(SpriteBatch spriteBatch){
foreach (DrawableGameObject dgo in ground)
dgo.Draw(spriteBatch);
}
}
Any ideas? I've posted the problem on Farseer's forum, but they haven't been very helpful yet...
This is a bug in Farseer. (Version 3.3.1)
I opened up the Farseer source code to the method in question (World.SolveTOI) and found two calls to Debug.Assert. And, in fact, in my copy of the code, I've actually already come across this bug and commented one of them out, specifically:
Debug.Assert(typeA == BodyType.Dynamic || typeB == BodyType.Dynamic);
Basically it doesn't want to attempt to handle contacts between two bodies that are static.
Fortunately the code immediately below actually checks for essentially the same condition, and continues the loop if that is the case:
bool awakeA = bA.Awake && typeA != BodyType.Static;
bool awakeB = bB.Awake && typeB != BodyType.Static;
// Is at least one body awake?
if (awakeA == false && awakeB == false)
{
continue;
}
So it's quite safe to simply comment out or remove the assertion. (You should, of course, be building Farseer from source - it makes life much easier.)
To reproduce the Farseer bug: Have two static bodies and one dynamic body that is in contact with both, then make the dynamic body static. The assert will trigger.
The assert is in the contact handling loop. Normally a pair of static bodies wouldn't create contacts. But if a body starts out as dynamic, contacts can be created - they don't get removed when the body is made static.
I'm creating a game in XNA that will work with lots of music loops over each other but I don't seem to be able to synchronize these sounds.
I always miss by a few milliseconds can you help me out?
This is my first try of synchronizing the sounds. Be aware I will need to work with dozens of sounds...
Might this sync problem be related with caching the sound?
Is there an external library to make it easier?
public SoundEffectInstance loop1, loop2;
public int auxControl = 0;
public bool auxB = false, auxA = false;
public void LoadContent()
{
SoundEffect temp1 = Game.Content.Load<SoundEffect>("sounds/Synctest_1");
loop1 = temp1.CreateInstance();
loop1.IsLooped = true;
loop2 = temp1.CreateInstance();
loop2.IsLooped = true;
}
public override void Update(GameTime gameTime)
{
// start first sound
if (auxA == false)
loop1.Play(); auxA = true;
// start counting until 2 seconds
if (auxA)
auxControl += gameTime.ElapsedGameTime.Milliseconds;
// if 2 seconds have passed after first sound start second sound
if (auxControl >= 2000 && auxB == false)
{
loop2.Play();
auxB = true;
}
base.Update(gameTime);
}
thank you
Don't know anything about C#, but usually it's hard to sync these sorts of things with millisecond accuracy if the API doesn't support it. The solution is to mix them yourself and use the API only for playback, that way you have control over exactly when they sounds play and how they are combined.
There may be a simpler solution in C#, but you can build something like this with http://www.PortAudio.com or a number of other interfaces. You may want to search for something like Game audio API on google.
For now I've decided that the best way to achieve this was using a condition inside the update, preferably the main update, so the verification is done the faster way it can be.
Still this brings a problem because you may hear a small "Tuk" between sounds but its hardly notice.
pseudo code would be something like this
Update(GameTime gameTime)
{
if (last sound ended)
{
play next;
decide on the next sound; // implement cache here if needed
}
}
I'm new to Reactive Extensions for .NET and while playing with it I thought that it would be awesome if it could be used for games instead of the traditional update-render paradigm. Rather than trying to call Update() on all game objects, the objects themselves would just subscribe to the properties and events they are interested in and handle any changes, resulting in fewer updates, better testability and more concise queries.
But as soon as, for example, a property's value changes, all subscribed queries will also want to update their values immediately. The dependencies may be very complex, and once everything is going to be rendered I don't know whether all objects have finished updating themselves for the next frame. The dependencies may even be such that some objects are continuously updating based on each other's changes. Therefore the game might be in an inconsistent state on rendering. For example a complex mesh that moves, where some parts have updated their positions and other have not yet once rendering starts. This would not have been a problem with the traditional update-render loop, as the update phase will finish completely before rendering starts.
So then my question is: is it possible to ensure that the game is in a consistent state (all objects finished their updates) just before rendering everything?
The short answer is yes, it is possible to accomplish what you're looking for regarding game update loop decoupling. I created a proof-of-concept using Rx and XNA which used a single rendering object which was not tied in any way to the game loop. Instead, entities would fire an event off to inform subscribers they were ready for render; the payload of the event data contained all the info needed to render a frame at that time for that object.
The render request event stream is merged with a timer event stream (just an Observable.Interval timer) to synchronize renders with the frame rate. It seems to work pretty well, and I'm considering testing it out on slightly larger scales. I've gotten it to work seemingly well both for batched rendering (many sprites at once) and with individual renders. Note that the version of Rx the code below uses is the one that ships with the WP7 ROM (Mirosoft.Phone.Reactive).
Assume you have an object similar to this:
public abstract class SomeEntity
{
/* members omitted for brevity */
IList _eventHandlers = new List<object>();
public void AddHandlerWithSubscription<T, TType>(IObservable<T> observable,
Func<TType, Action<T>> handlerSelector)
where TType: SomeEntity
{
var handler = handlerSelector((TType)this);
observable.Subscribe(observable, eventHandler);
}
public void AddHandler<T>(Action<T> eventHandler) where T : class
{
var subj = Observer.Create(eventHandler);
AddHandler(subj);
}
protected void AddHandler<T>(IObserver<T> handler) where T : class
{
if (handler == null)
return;
_eventHandlers.Add(handler);
}
/// <summary>
/// Changes internal rendering state for the object, then raises the Render event
/// informing subscribers that this object needs rendering)
/// </summary>
/// <param name="rendering">Rendering parameters</param>
protected virtual void OnRender(PreRendering rendering)
{
var renderArgs = new Rendering
{
SpriteEffects = this.SpriteEffects = rendering.SpriteEffects,
Rotation = this.Rotation = rendering.Rotation.GetValueOrDefault(this.Rotation),
RenderTransform = this.Transform = rendering.RenderTransform.GetValueOrDefault(this.Transform),
Depth = this.DrawOrder = rendering.Depth,
RenderColor = this.Color = rendering.RenderColor,
Position = this.Position,
Texture = this.Texture,
Scale = this.Scale,
Size = this.DrawSize,
Origin = this.TextureCenter,
When = rendering.When
};
RaiseEvent(Event.Create(this, renderArgs));
}
/// <summary>
/// Extracts a render data object from the internal state of the object
/// </summary>
/// <returns>Parameter object representing current internal state pertaining to rendering</returns>
private PreRendering GetRenderData()
{
var args = new PreRendering
{
Origin = this.TextureCenter,
Rotation = this.Rotation,
RenderTransform = this.Transform,
SpriteEffects = this.SpriteEffects,
RenderColor = Color.White,
Depth = this.DrawOrder,
Size = this.DrawSize,
Scale = this.Scale
};
return args;
}
Notice that this object doesn't describe anything how to render itself, but only acts as a publisher of data that will be used in rendering. It exposes this by subscribing Actions to observables.
Given that, we could also have an independent RenderHandler:
public class RenderHandler : IObserver<IEvent<Rendering>>
{
private readonly SpriteBatch _spriteBatch;
private readonly IList<IEvent<Rendering>> _renderBuffer = new List<IEvent<Rendering>>();
private Game _game;
public RenderHandler(Game game)
{
_game = game;
this._spriteBatch = new SpriteBatch(game.GraphicsDevice);
}
public void OnNext(IEvent<Rendering> value)
{
_renderBuffer.Add(value);
if ((value.EventArgs.When.ElapsedGameTime >= _game.TargetElapsedTime))
{
OnRender(_renderBuffer);
_renderBuffer.Clear();
}
}
private void OnRender(IEnumerable<IEvent<Rendering>> obj)
{
var renderBatches = obj.GroupBy(x => x.EventArgs.Depth)
.OrderBy(x => x.Key).ToList(); // TODO: profile if.ToList() is needed
foreach (var renderBatch in renderBatches)
{
_spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
foreach (var #event in renderBatch)
{
OnRender(#event.EventArgs);
}
_spriteBatch.End();
}
}
private void OnRender(Rendering draw)
{
_spriteBatch.Draw(
draw.Texture,
draw.Position,
null,
draw.RenderColor,
draw.Rotation ?? 0f,
draw.Origin ?? Vector2.Zero,
draw.Scale,
draw.SpriteEffects,
0);
}
Note the overloaded OnRender methods which do the batching and drawing of the Rendering event data (it's more of a message, but no need to get too semantic!)
Hooking up the render behavior in the game class is simply two lines of code:
entity.AddHandlerWithSubscription<FrameTicked, TexturedEntity>(
_drawTimer.Select(y => new FrameTicked(y)),
x => x.RaiseEvent);
entity.AddHandler<IEvent<Rendering>>(_renderHandler.OnNext);
One last thing to do before the entity will actually render is to hook up a timer that will serve as a synchronization beacon for the game's various entities. This is what I think of as the Rx equivalent of a lighthouse pulsing every 1/30s (for default 30Hz WP7 refresh rate).
In your game class:
private readonly ISubject<GameTime> _drawTimer =
new BehaviorSubject<GameTime>(new GameTime());
// ... //
public override Draw(GameTime gameTime)
{
_drawTimer.OnNext(gameTime);
}
Now, using the Game's Draw method may seemingly defeat the purpose, so if you would rather avoid doing that, you could instead Publish a ConnectedObservable (Hot observable) like this:
IConnectableObservable<FrameTick> _drawTimer = Observable
.Interval(TargetElapsedTime)
.Publish();
//...//
_drawTimer.Connect();
Where this technique can be incredibly useful is in Silverlight-hosted XNA games. In SL, the Game object is unavailable, and a developer needs to do some finagling in order to get the traditional game loop working correctly. With Rx and this approach, there is no need to do that, promising a much less disruptive experience in porting games from pure XNA to XNA+SL
This is potentially quite a general question about decoupling rendering from update in a game loop. This is something that networked games have to cope with already; "how do you render something that doesn't break the player's immersion when you don't actually know what's happened yet?"
One approach to this is to 'multi buffer' the scene graph, or elements of it, and actually render an interpolated version at a higher render frame rate. You still have to identify a point in your update when everything is finished for a particular time step, but it is no longer tied to the render. Instead you copy your update results to a new scene graph instance with a time stamp and get started on the next update.
It does mean that you are rendering with a lag, so may not be suitable for all types of game.
Why don't you use some kind of IScheduler to schedule your change subscriptions. Then you could have your main game loop step your scheduler implementation 16.6 ms every frame (assuming 60fps). The idea would be that it would execute any scheduled actions due in that time, so you could still use things like delay or throttle.