Setting target field values - c#

I'm making a game, in which I have various fields that I'd like to set target values for. For example, my Camera class has:
public double zoomLevel
Currently, if the zoomLevel is (say) 1.0 and I'd like to increase it gradually to (say) 2.0, I have the following other fields to support this:
private double targetZoomLevel
private double zoomIncrement
I then have a Camera.SetZoom(double target, double increment) method that sets a desired furure zoom level, and then a Camera.Update() method that moves the current zoom level towards the target level, using the increment.
This all works well enough, but I'd really like to implement the same behaviour for other fields (e.g. camera world position, player size, player position, etc.). Using my current method, I'd need to add 2 additional 'support' fields for each field.
I'm pretty sure that my current solution is a sub-optimal, but not sure how to go about improving this. I was thinking about implementing a Property<T> class that encapulates this behaviour for a value, but not sure how to generalise an Update() method to move the current value towards its target.
Thanks!

What you're describing sounds very much like an animation. Animations are formal concepts in a couple of frameworks (CSS and WPF come to mind).
The goal of an animation is to transition something from one value to the next.
There are a variety of ways to make that transition. Sometimes you want a 2D point to follow a Bézier curve as the curve's t variable linearly changes from 0 to 1 over some period of time. Other times you want a color to transition smoothly from red to blue by going the long way around a color wheel.
Here's an interface that can abstract over that concept:
public interface IAnimation<T>
{
T Current { get; }
void Update(double progress); // progress is a number between 0 and 1
}
For example, if you want a 2D point to animate over a sine wave 800 units wide and 2 units tall:
public sealed class Point2DAnimation : IAnimation<Point2D>
{
public Point2D Current { get; private set; }
public void Update(double progress)
{
Current = new Point2D(progress * 800, Math.Sin(progress * Math.PI * 2));
}
}
There are also a variety of ways to drive the transition. A common way to drive it is to say "I want this animation to happen as smoothly as possible over X seconds". But sometimes you might want the animation to repeat from the beginning a few more times. Or perhaps you want the animation to run forward then backward then forward and so on forever.
We could define an interface to abstract over the different ways that an IAnimation<T> can be driven. And one implementation might internally have a timer that ticks frequently enough to give the illusion of smoothness as progress goes from 0 to 1 in proportion to the amount of time that has passed at the moment of each tick.
But I want to pause here for a moment and ask a question.
What does the consumer of the abstraction need from it?
If all your code needs is read access to a T and a method called Update(), then Property<T> sounds like your ticket:
public sealed class Property<T>
{
readonly Func<T, T> _update;
public Property(T initial, Func<T, T> update)
{
Value = initial;
_update = update;
}
public T Value { get; private set; }
public void Update()
{
Value = _update(Value);
}
}
That class encapsulates a readable value and gives you an Update() method to call. No need for an IAnimation<T> interface or any other fancy trappings. Of course you'll have to instantiate instances of Property<T> with an update delegate that does what you want. But you can add some static methods somewhere to set it up in the most common ways. For example:
public static class PropertyHelpers
{
public Property<double> CreateZoomLevelProperty(double initial, double increment, double target)
{
return new Property<double>(initial, old =>
{
var #new = old + increment;
if (increment > 0 && #new > target || increment < 0 && #new < target)
return target;
else
return #new;
});
}
}
On the other hand if you want to think of the value as a stream of values controlled by something else in the code and you only need to get read access to the values when they arrive, then perhaps IObservable<T> (and the Reactive NuGet package) is what you're after. For example:
public class Camera
{
double _zoomLevel;
public IDisposable UpdateZoomLevel(IObservable<double> zoomLevels)
{
return zoomLevels.Subscribe(zoomLevel => _zoomLevel = zoomLevel);
}
}
Of course then it becomes the responsibility of other code somewhere to come up with an IObservable<double> that publishes zoom levels in the fashion that you desire.
So I suppose my answer can be summed up as this:
It depends.
Disclaimer: Perhaps none of the code above will compile

Related

How can I serialize a condition in Unity Inspector?

I have a list of different creatures in a game, each of these animals will be unlocked if a specific condition is reached, of course the simple solution is to use an enum to categorize the creatures, but I want to be able to set the conditions themselves in inspector.
Manager.cs:
public Creature[] creatures;
public void Start()
{
foreach (var creature in creatures)
{
if (creature.condition()) creature.Unlock();
}
}
For example, to unlock Ogre creature, your player maximum life must be above 120, and to unlock Bat, your character must be faster than 80 move speed. One solution that has come to my mind so far is to serialize Func<bool>, But it does not work and I can not change the condition from the inspector. What is the best solution to this problem? Every suggestion is appreciated.
[SerializeField]
public Func<bool> condition = () => (player.maxHealth > 120);

Create animations programmatically in Unity?

In my game I have a big catalog of gear: Armors, weapons and shields. The combinations between these can be really immense.
Besides that, the player has the option of switching in-game to a different set of armor-weapon combination. In the end to solve this, I have used the following object structure.
Whenever I switch the weapons, I activate/deactivate the necessary GameObjects. The animations are set in this way:
Now, the problem is creating the animation. I first considered pre-rendering programatically all the combinations, but my catalog is so huge, that it would create 100s, if not 1000s of animations. So I opted for a different solution. Create in playtime the animation, once I knew what gear would the player select. For that, I created a script to take care of that. The problem is that I have been using APIs from UnityEditor, and now I have realized the build will not work. Specifically because of 2 different classes: EditorCurveBinding and ObjectReferenceKeyframe.
This is a couple snippets of how I was using this classes when creating the animations:
static EditorCurveBinding GetEditorCurveBinding(string path = "")
{
EditorCurveBinding spriteBinding = new EditorCurveBinding();
spriteBinding.type = typeof(SpriteRenderer);
spriteBinding.path = path;
spriteBinding.propertyName = "m_Sprite";
return spriteBinding;
}
static ObjectReferenceKeyframe GetKeyframe(float time, Sprite sprite)
{
ObjectReferenceKeyframe keyframe = new ObjectReferenceKeyframe();
keyframe.time = time / FRAMERATE;
keyframe.value = sprite;
return keyframe;
}
Now, the problem with the Curve, I think I managed to solve, replacing it with this code, replacing EditorCurveBinding with AnimationCurve:
AnimationClip clip = ...
AnimationCurve curve = new AnimationCurve();
clip.SetCurve(path, typeof(SpriteRenderer), "m_Sprite", curve);
But I have no idea how to set the sprites for each animation. I thought that using curve.AddKeycould be helpful, but I have seen no way to add a sprite there.
How could I rewrite that code to avoid using UnityEditor?
Full code
Personally, I would completely avoid built-in Animator and Animations, since this tool is for the very narrow purpose of animating a single object in a predefined way (eg: for a cutscene).
Offtopic - performance
Besides that, the player has the option of switching in-game to a different set of armor-weapon combination. In the end to solve this, I have used the following object structure.
As you probably know this is very inefficient memory-wise and will decrease performance (disabled objects still have marginal CPU overhead). And will incread load time and instantiation time of new objects.
Can I use Animator or Animation?
Because Animator has no API to access it's internals and animation type and overall you cannot do pretty mutch nothing with it except from telling it to Play or Stop.
Since I understand that your animation is "Sprite based" and not "Transform based" any sane idea is just inefficient!
Best solution that I vow against is as follows:
Create "trigger points" that you would "animate"
Based on trigger point - change sprite
public class AnimationController : MonoBehaviour
{
public int AnimationIndex;
public Sprite[] AnimationSprites;
public SpriteRenderer SpriteRenderer;
private void Update()
{
SpriteRenderer.sprite = AnimationSprites[AnimationIndex];
}
}
Better solution?
Since we already need to have custom structure that manages our sprites we might want to optimize the whole thing, since we are not using almost any of the features of Animator we could write custom controller that would replace Animator in this case. This should improve performance significantly since Animator is very heavy!
// MonoBehaviour is optional here
public class SpriteRendererAnimationHandler
{
// More fields that would control the animation timing
public Sprite[] AnimationSprites;
public SpriteRenderer SpriteRenderer;
public void OnAnimationUpdate(int index)
{
var resolvedIndex = ResolveIndex(index);
SpriteRenderer.sprite = AnimationSprites[resolvedIndex];
}
private int ResolveIndex(int index)
{
// Resolve animation index to sprite array index
}
}
// One controller per character - or global per game that synchronize all animations to locked FPS 12.
public class AnimationController : MonoBehaviour
{
private List<SpriteRendererAnimationHandler> Handlers = new List<SpriteRendererAnimationHandler>();
public void FixedUpdate()
{
foreach (var handler in Handlers)
{
// Calculate animation index
int calculatedAnimationIndex = ...;
handler.OnAnimationUpdate(calculatedAnimationIndex);
}
}
}
Add a public field named animation index to Tomasz last example, then create the animations in animator as youd normal do for animation pieces, then animate that animation index field. simple if(currentAnimationyion != lastAnimationIndex) to check if need to send to handlers and ya rocking

How can I swap the positions of two objects at different speeds, instead of an instant swap?

I have a script that currently swaps the position of 2 objects. The objects are the same size and shape so that when they swap positions, it's instant and you don't see them move toward the position. How can I make it so there is a movement speed when the two objects swap places, so that you can see them actually moving positions. I also need to figure out how to add a third object so that they all switch positions randomly. Something like this is my final goal: http://i249.photobucket.com/albums/gg240/OldNewby4507/shell-game-animated.gif
using UnityEngine;
using System.Collections;
public class NewBehaviourScript : MonoBehaviour
{
public float movementSpeed = 10;
public GameObject g1;
public GameObject g2;
public Vector3 vec1;
public Vector3 vec2 = new Vector3(2F, 2F, 2F);
public bool swapBack = false;
void Start()
{
g1 = GameObject.Find("object1");
g2 = GameObject.Find("object2");
vec1 = new Vector3(g1.gameObject.transform.position.x, g1.gameObject.transform.position.y, g1.gameObject.transform.position.z);
vec2 = new Vector3(g2.gameObject.transform.position.x, g2.gameObject.transform.position.y, g2.gameObject.transform.position.z);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
vec1 = g1.gameObject.transform.position;
vec2 = g2.gameObject.transform.position;
g1.gameObject.transform.position = vec2;
g2.gameObject.transform.position = vec1;
}
}
}
right now my movementSpeed variable is unused.
The way i would tackle this is the following:
You need to create a function which is called, for example, MovePath. MovePath will be governed by a formula that determines how the object moves from point A to point B (this can be linearly, exp, log, etc). MovePath takes three parameters, vector Start, vector End and int or double MoveSpeed. To get from point A to point B will take steps equal to MoveSpeed. So you are calculating positions at each step of the way.
Update will need to be modified so that it takes int MoveSpeed as a parameter. Update will keep updating by using the movepath method until vector start becomes vector end. You will be calling MovePath twice (one for each object thats moving) until for g1's position becomes g2's and g2's position becomes g1's. Update will track when G1 == G2 and G2 == G1 with respect to the positions and stop updating once that is complete.
You will need to implement async and await so that the UI can update. There might be a way to do this synchronously but i think going the async and await path will be much cleaner in the long run.
Please let me know if you have questions when you try to implement this. Once you try to implement this edit your question and I think people (and myself) will be able to chime in with issues you are running into when implementing this.
Edit: there are a lot of ways to implement this. An other example is you can change the MovePath so that it calls itself recursively until the end condition is met. I just wanted to provide you with an idea on how to tackle it.
You can Lerp the transition at difference rates, which is quite linear, if you would like you can also use Slerp or any of the other Vector3 methods.
You can also use libraries that offer Tweening operations such as iTween amongst a bunch of other ones on the asset store which will take care of the transition for you.
I would use Lerp, or Slerp. These are interpolation methods using math, and are quite simple and seamless. Your code would look something like this:
void Update() {
if (Input.GetMouseButtonDown(0)) {
vec1 = g1.gameObject.transform.position;
vec2 = g2.gameObject.transform.position;
g1.gameObject.transform.position = Vector3.Lerp(vec1, vec2, 0.5f);
g2.gameObject.transform.position = Vector3.Lerp(vec2, vec1, 0.5f);
}
}
Here is an excellent explanation of Lerp, it may help you in this case!

Unity - Resizing one GameObject to match another

I'm working on a project in Unity that involves regions that teleport any non-static object from one to the paired. That part's fine, but for convenience, I'm trying to write a part of the script that will resize one object if its pair is resized, such that they will always be of equal size. And so far it works - mostly. The only problem I encounter is when trying to resize through the Transform component - as in, typing in numbers in Inspector, or using the value sliders on X or Y or Z. The handles work fine. It's not a big deal, I suppose, but if I could figure out why this isn't working, so I can learn what to do in the future, I'd be very glad. Here's the code:
[ExecuteInEditMode]
public class TransferRegion : MonoBehaviour {
// Unrelated code...
public bool scaleManuallyAltered {
get; private set;
}
[SerializeField]
private TransferRegion pair;
private Vector3 scale;
// Called whenever the scene is edited
void Update () {
if (scale != gameObject.transform.localScale) {
scaleManuallyAltered = true;
scale = gameObject.transform.localScale;
}
if (pair && scaleManuallyAltered && !pair.scaleManuallyAltered) {
pair.transform.localScale = scale;
}
}
// Called AFTER every Update call
void LateUpdate () {
scaleManuallyAltered = false;
}
// Unrelated code...
}
If anyone can see some major logical failure I'm making, I'd like to know. If my code's a bit hard to understand I can explain my logic flow a bit, too, I know I'm prone to making some confusing constructs.
Thanks folks.
If you want one object to be the same scale as another, why not just simplify your code by setting the scale of the re sizing game object, directly to the scale of the game object it is based off of? For example, this script re sizes an object to match the scale of its pair while in edit mode:
using UnityEngine;
using UnityEditor;
using System.Collections;
[ExecuteInEditMode]
public class tester : MonoBehaviour
{
public Transform PairedTransform;
void Update()
{
if (!Selection.Contains(gameObject))
{
gameObject.transform.localScale = PairedTransform.localScale;
}
}
}
I tested this on two cubes in my scene. I was able to resizing using gizmos as well as manually typing in numbers to the transform edit fields in the inspector.
Edit: By taking advantage of Selection you can apply the scale change only to the object in the pair that is not selected in the hierarchy. This way the pairs wont be competing with each other to re scale themselves.
So I figured it out.
I'm not sure what was wrong with my original code, but eventually I decided to slip into the realm of good old handy events:
[ExecuteInEditMode]
public class TransferRegion : MonoBehaviour {
//...
[SerializeField]
private UnityEvent rescaled;
//...
void Update() {
if (scale != gameObject.transform.localScale) {
scale = gameObject.transform.localScale;
rescaled.Invoke();
}
}
//...
public void OnPairRescaled() {
gameObject.transform.localScale = pair.transform.localScale;
scale = gameObject.transform.localScale;
}
}
And just set the OnPairRescaled event to be the listener for the paired object's rescaled event.
Ta-da!

Score not changing when collision occurs

I have been trying to get my score to work correctly when the player collides with one of the collectibles but the score does not seem to change when the collision occurs, I am not sure why this is happening.
In my collectible I have this:
class BlueBall : Obj
{
public BlueBall(Vector2 pos)
: base(pos)
{
position = pos;
spriteName = "BlueBall";
solid = false;
score = 0;
}
public override void Update()
{
if (!alive) return;
Player player = CheckCollisionAgainst<Player>();
if (player != null)
{
score = score + 20;
alive = false;
}
I am drawing in the Game1 class with:
spriteBatch.DrawString(font, "Score: " + score.ToString(), scorePos, Color.White);
So if the Player Collides with the BlueBall, 20 should be added to the score and the BlueBall should disappear, it does disappear but the score does not change, why is this?
At this moment I am declaring my score in the Game1 class with public int score and Vector2 scorePos to position it. I then Initialize the score with score = 0; and then Load in the scorePos values in Update.
From your code, and your comments, it is clear that there are actually two score variables. One that is owned by the game:
public class Game
{
private int score;
void Draw(...)
{
spriteBatch.DrawString(font, "Score: " + score.ToString(), scorePos, Color.White);
}
}
and one that is owned by the ball:
public class BlueBall : Obj
{
private int score;
public void CheckCollisions()
{
if (collision)
score += 20;
}
}
Now, those two score variables have absolutely no relation to each other. The fact that they are named the same is totally irrelevant.
To make a quick analogy, imagine programming objects are boxes. Each variable is a "counter". You put a counter in the "Game" box and call it "score". You put another counter in the "Ball" box and also call it "score". Obviously, modifying the counter in the "Ball" box won't affect the one in the "Game" box and vice versa. In general, this concept is called "scoping" in programming. Each variable is scoped to its box, which can be a class, function, or even a using block (scopes are created by {}).
Now that you understand the problem, there are a couple ways to fix it:
Create a global variable. You can't actually do that in C#, but you can create another object that both other objects have access to. Both objects than reference/modify it's "score" property. A simple (and poor practice) example would be:
public static class GlobalState
{
public static int GameScore { get; set; }
}
All references to score would become GlobalState.GameScore. Again, this is just to get you going, this kind of code is really bad practice, and can cause bugs (especially in multi-threaded situations).
Have the ball raise an event on collision. The game would register for this event and increment its "score" variable appropriately.
Pass the game's "score" variable to the collision method by ref, so that it can change it. Warning: This is included for completeness, and this should only be done in specific circumstances, of which yours is probably not one of them.
Pass a delegate to the ball on creation to invoke when it needs to increment the score. This is really a variation of the event method.
I'm sure there are others. Given your experience level, I would start with the global approach (as its the simplest and requires the least amount of knowledge) and then move to learning about events and go with that approach in the future.

Categories