I'm working on a random wave system for a game. The idea is that every 1000 points a movement pattern would be selected from around 50 possibilities. This would affect the speed, direction, and image of the selected item. I have devised a method that I think will work, but I'm unsure if this is going to cost too much memory to run.
public class engine extends MovieClip {
private var countK:Number = 0;
private var newWave:Boolean = true;
public function engine() {
stage.addEventListener(Event.ENTER_FRAME, update);
}
private function update():void {
checkCount();
checkNew();
}
private function checkCount():void {
if (count => 1000) {
newWave=true;
count = 0;
}
}
private function checkNew():void {
if(newWave) {
randomNumber();
newWave=false
}
}
Above is my quick idea of getting a random number to be generated every 1000 points. Points can be added in any way you want (just add say 20 to "Score" and 20 to "count" at the same time). Where I can a random number function in checkNew, I won't be pulling another function, it's simply there for the sake of legibility.
var newEnemy:mEnemy =new mEnemy();
stage.addChild(newEnemy);
EnemyArray.push(newEnemy);
trace(EnemyArray.length);
Above is some code that can add an instance of mEnemy to the stage. Now where I'm starting to loose it is, how can I translate the random number into a viable method of changing mEnemy's behaviour?
Is it wise to have 50 functions inside the mEnemy class and just before I addChild, I do something like newEnemy.WAVEfuncton1(); ? If that is the case, can I save code by getting it to select the function without writing a whole bunch of if statements?
Instead of;
if (randomN==1) {
newEnemy.WAVEfunction1();
}
if (randomN==2) {
newEnemy.WAVEfunction2();
}
....
Can I do;
newEnemy.WAVEfunction[randomN]();
This is also assuming that using functions inside the enemy is the best idea. Is it better to have the behaviours inside the engine class instead?
As you can see, I'm no programmer. I'm very new to this sort of thinking and I don't want to create a mistake that will destroy the performance of the game (not to mention picking up bad habits too!).
If you have taken the time to read this question, thank you! If you tolerate my ignorance, then thank you even more!
If the wave functions are just creating a single enemy of a certain type, it might make more sense to make an array with the details of each type like this: (I'm guessing at how your enemies work of course)
private const ENEMY_TYPES:Array = [
{speed:1, direction:90, image:1},
{speed:2, direction:45, image:2}
]
then change mEnemy() to set itself up according to the details you give it:
public function mEnemy(details:Object) {
mySpeed = details.speed;
...
That way, you can just write new mEnemy(ENEMY_TYPES[randomN]);
Alternatively, if you do need to have lots of separate wave functions, you can use the [ ] array access operator to access the properties of an object such as newEnemy by name (or this to reference the current object):
var exampleProperty:String = "Hello.";
this["exampleProperty"];
So you can run your wave functions by writing:
newEnemy["WAVEfunction" + String(randomN)]();
A 2-year old question and rather non-actual already but let me try myself here as I have just signed up.
As I understood, what are you proposing to do here is writing all 50 behaviour methods for each kind of Enemy, which is of course not good.
First, you can add the "behaviour" entity. So each enemy now has a behaviour property.
Next, you have to create a separate Behaviour class or interface, which will have 50 subclasses (Behaviour1...Behaviour50), each subclass implementing its own run() method. Note that this way you will be able to add or remove behaviours without touching anything else. A basic implementation would look like this:
public class Behaviour() {
public function run(e:Enemy):void {
e.y += 10;
}
}
So you see, it's not like enemy is doing something. It's the Behaviour that does something with the enemy it was passed to.
Next, you need a mechanism to get the proper subclass from a given random number.
What you need is a Factory - a static class that will return different types of Behaviours based on input params. Something like this:
public class BehaviourFactory {
public static getBehaviour(n:int):Behaviour {
switch(n) {
case 1: return new Behaviour1();
case 2: return new Behaviour2();
// etc.
}
}
}
Instead of having 50 choices inside a switch, you can also use the class definition:
var c:Class = getDefinitionByName('Behaviour' + your_random_number) as Class;
return new c;
(In further implementatons it can be cached, stored in an Array etc.) After you have a Factory, you just do:
var b:Behaviour = BehaviourFactory.getBehaviour(your_random_number);
Next, you can use different approaches depending of how exactly the behaviour changes. For example, if the enemy is born with a specific current behaviour and it doesn't change during the enemy's lifetime, you can just assign one of Behaviour subclasses to the Enemy's behaviour property:
public class Enemy {
public var behaviour:Behaviour;
public function Enemy(b:Behaviour) {
this.behaviour = b;
}
}
var e:Enemy = new Enemy(BehaviourFactory.getBehaviour(random_number));
e.behaviour.run(e);
This property of course can also be changed dynamically so the next time it is run the enemy will behave differently.
If the behaviour is global for all enemies and changes for all of them at once, you don't event need to have a property in an Enemy object. You just have a global Behaviour object and pass there an Enemy instance:
var e:Enemy = enemy_list[i];
current_behaviour.run(e);
it will take care of processing each active enemy according to the currently chosen behaviour.
Finally, there's more interesting way to implement behaviours. Suppose you have several behaviour types that don't have anything in common. Say, the Enemy can be Crawling, Flying, Shooting and Poisonous. So let's say you're attempting to implement all possible combinations: Flying, FlyingShooting, FlyingPoisonous, FlyingShootingPoisonous, etc. You would have to create a Behaviour subclass for each of these combinations despite them having very common basic parts.
There's an other way to go, called the Decorator pattern. You simply write a method for each single quality. Whenever you need a combination of qualities, you simply create object with first quality and wrap it into the object with the second quality and wrap it into the object with the third quality etc. So your base Behaviour class needs one addition:
public class Behaviour {
private var parent_bhv: Behaviour;
public function Behaviour(bhv:Behaviour = null) {
if (bhv) this.parent_bhv = bhv;
}
public function run(e:Enemy):void {
e.y += 10; // do what we need to do
if (this.parent_bhv) this.parent_bhv.run(e); // pass to a next bhv.
}
}
Let's create compound behaviour of number 1, 3 and 15:
var decorated_behaviour:Behaviour = BehaviourFactory.getDecoratedBehaviour([1, 3, 15]);
let's also add the corresponding BehaviourFactory method:
public class BehaviourFactory {
public static function getDecoratedBehaviour(bhv_list:Array):Behaviour {
var b:Behaviour = null;
for (var i:int = 0; i < bhv_list.length; i++) {
var c:Class = getDefinitionByName('Behaviour' + bhv_list[i]) as Class;
b = new c(b);
}
return b;
}
}
Now you're all set without having to code all possible combinations!
Related
I'm working towards writing a script to take a "snapshot" of the initial attributes of all children of a GameObject. Namely at startup I want to save the position, orientation & color of all these objects in a Hashtable. The user has the ability to move & modify these objects during runtime, and I want to update the Hashtable to keep track of this. This will allow me to create an Undo last action button.
I found that gameObject.name isn't a good Key for my Hashtable entries because sometimes multiple game objects have the same name (like "cube"). So what would make a better Key? It's clear that Unity differentiate between two identical game objects with the same name, but how? I don't want to have to manually Tag every game object. I want to eventually bring in a large CAD file with hundreds of parts, and automatically record them all in a Hashtable.
For example, the code below works fine, unless I have multiple game objects with the same name. Then I get this error ArgumentException: Item has already been added. Key in dictionary: 'Cube' Key being added: 'Cube'
public class GetAllObjects : MonoBehaviour
{
public Hashtable allObjectsHT = new();
void Start()
{
Debug.Log("--Environment: GetAllObjects.cs <<<<<<<<<<");
foreach (Transform child in transform)
{
allObjectsHT.Add(child.gameObject.name, child);
}
}
}
Thanks Chuck this is what I want, and you solved my problem:
public class GetAllObjects : MonoBehaviour
{
UnityEngine.Vector3 startPosition;
UnityEngine.Quaternion startRotation;
public Hashtable allObjectsHT = new();
void Start()
{
Debug.Log("--Environment: GetAllObjects.cs <<<<<<<<<<");
foreach (Transform child in transform)
{
startPosition = child.position;
startRotation = child.rotation;
Hashtable objHT = new();
objHT.Add("position", startPosition);
objHT.Add("rotation", startRotation);
allObjectsHT.Add(child, objHT);
}
}
}
It's good to use meaningful keys you can refer to, otherwise you'd just use a collection without keys like a List. You could use an editor script to name all of the objects you import and use the names as keys. e.g.
int i = 0;
foreach(GameObject g in Selection.gameObjects)
{
g.name = "Object_" + i.ToString();
i++;
}
You could make the naming more sophisticated and meaningful of course, this is just an example.
I have a delegate that takes two numbers and creates a System.Windows.Point from them:
(x, y) => new Point(x,y);
I want to learn how can I use TPL Dataflow, specifically TransformBlock, to perform that.
I would have something like this:
ISourceBlock<double> Xsource;
ISourceBlock<double> Ysource;
ITargetBlock<Point> PointTarget;
// is there such a thing?
TransformBlock<double, double, Point> PointCreatorBlock;
// and also, how should I wire them together?
UPDATE:
Also, how can I assemble a network that joins more than two arguments? For example, let's say I have a method that receives eight arguments, each one coming from a different buffer, how can I create a block that knows when every argument has one instance available so that the object can be created?
I think what your looking for is the join block. Currently there is a two input and a three input variant, each outputs a tuple. These could be combined to create an eight parameter result. Another method would be creating a class to hold the parameters and using various block to process and construct the parameters class.
For the simple example of combining two ints for a point:
class MyClass {
BufferBlock<int> Xsource;
BufferBlock<int> Ysource;
JoinBlock<int, int> pointValueSource;
TransformBlock<Tuple<int, int>, Point> pointProducer;
public MyClass() {
CreatePipeline();
LinkPipeline();
}
private void CreatePipeline() {
Xsource = new BufferBlock<int>();
Ysource = new BufferBlock<int>();
pointValueSource = new JoinBlock<int, int>(new GroupingDataflowBlockOptions() {
Greedy = false
});
pointProducer = new TransformBlock<Tuple<int, int>, Point>((Func<Tuple<int,int>,Point>)ProducePoint,
new ExecutionDataflowBlockOptions()
{ MaxDegreeOfParallelism = Environment.ProcessorCount });
}
private void LinkPipeline() {
Xsource.LinkTo(pointValueSource.Target1, new DataflowLinkOptions() {
PropagateCompletion = true
});
Ysource.LinkTo(pointValueSource.Target2, new DataflowLinkOptions() {
PropagateCompletion = true
});
pointValueSource.LinkTo(pointProducer, new DataflowLinkOptions() {
PropagateCompletion = true
});
//pointProduce.LinkTo(Next Step In processing)
}
private Point ProducePoint(Tuple<int, int> XandY) {
return new Point(XandY.Item1, XandY.Item2);
}
}
The JoinBlock will wait until it has data available on both of its input buffers to produce an output. Also, note that in this case if X's and Y's are arriving out of order at the input buffers care needs to be taken to re-sync them. The join block will only combine the first X and the first Y value it receives and so on.
I am testing my code with PHPunit. My code has several ordering-methods: by name, age, count and random. Below the implementation and test for sorting by count. These are pretty trivial.
class Cloud {
//...
public function sort($by_property) {
usort($this->tags, array($this, "cb_sort_by_{$by_property}"));
return $this;
}
private function cb_sort_by_name($a, $b) {
$al = strtolower($a->get_name());
$bl = strtolower($b->get_name());
if ($al == $bl) {
return 0;
}
return ($al > $bl) ? +1 : -1;
}
/**
* Sort Callback. High to low
*/
private function cb_sort_by_count($a, $b) {
$ac = $a->get_count();
$bc = $b->get_count();
if ($ac == $bc) {
return 0;
}
return ($ac < $bc) ? +1 : -1;
}
}
Tested with:
/**
* Sort by count. Highest count first.
*/
public function testSortByCount() {
//Jane->count: 200, Blackbeard->count: 100
//jane and blackbeard are mocked "Tags".
$this->tags = array($this->jane, $this->blackbeard);
$expected_order = array("jane", "blackbeard");
$given_order = array();
$this->object->sort("count");
foreach($this->object->get_tags() as $tag) {
$given_order[] = $tag->get_name();
}
$this->assertSame($given_order, $expected_order);
}
But now, I want to add "random ordering"
/**
* Sort random.
*/
public function testSortRandom() {
//what to test? That "shuffle" got called? That the resulting array
// has "any" ordering?
}
The implementation could be anything from calling shuffle($this->tags) to a usort callback that returns 0,-1 or +1 randomly. Performance is an issue, but testability is more important.
How to test that the array got ordered randomly? AFAIK it is very hard to stub global methods like shuffle.
Assuming you are using shuffle your method should look like this
sortRandom() {
return shuffle($this->tags);
}
Well, you don't need to test if keys are shuffled but if array is still returned.
function testSortRandom(){
$this->assertTrue(is_array($this->object->sortRandom()));
}
You should test your code, not php core code.
This is actually not really possible in any meaningful sense. If you had a list with just a few items in, then it'd be entirely possible that sorting by random would indeed look like it's sorted by any given field (and as it happens the odds of it being in the same order as sorting by any other field are pretty high if you don't have too many elements)
Unit testing a sort operation seems a bit daft if you ask me though if the operation doesn't actually manipulate the data in any way. Feels like unit testing for the sake of it rather than because it's actually measuring that something works as intended.
I decided to implement this with a global-wrapper:
class GlobalWrapper {
public function shuffle(&$array);
shuffle($array);
}
}
In the sort, I call shuffle through that wrapper:
public function sort($by_property) {
if ($by_property == "random") {
$this->global_wrapper()->shuffle($this->tags);
}
//...
}
Then, in the tests I can mock that GlobalWrapper and provide stubs for global functions that are of interest. In this case, all I am interested in, is that the method gets called, not what it outputs[1].
public function testSortRandomUsesShuffle() {
$global = $this->getMock("GlobalWrapper", array("shuffle"));
$drupal->expects($this->once())
->method("shuffle");
$this->object->set_global_wrapper($drupal);
$this->object->sort("random");
}
[1] In reality I have Unit Tests for this wrapper too, testing the parameters and the fact it does a call-by-ref. Also, this wrapper was already implemented (and called DrupalWrapper) to allow me to stub certain global functions provided by a third party (Drupal). This implementation allows me to pass in the wrapper using a set_drupal() and fetch it using drupal(). In above examples, I have called these set_global_wrapper() and global_wrapper().
Here's the easy pseudo-code:
void TextBox1Changed()
{
//If the text isn't a number, color it red
if (!IsValidNumber(TextBox1.Text)
TextBox1.Color = Pink;
else
TextBox1.Color = WindowColor;
}
What's the MVC enterprisey version?
Not trying to be language specific, but the idea is to create a number text control that knows if the value is valid. It's easy to get hung up on the exact roles of M, V, and C. However, for all practical purposes, it makes sense to combine the View and the Controller for Desktop like applications. Swing took that approach because the controller and view had very tight coupling and it made sense to combine them into one. Read up this nice discussion on c2 about the topic.
class NumberTextBox extends TextBox {
bool isValid() {
return IsValidNumber(this.Value);
}
}
ageTextBox = new NumberTextBox();
ageTextBox.addChangeHandler(function() {
this.Color = this.isValid ? WindowColor : Pink;
});
I'd like to create an IList<Child> that maintains its Child objects in a default/implicit sort order at all times (i.e. regardless of additions/removals to the underlying list).
What I'm specifically trying to avoid is the need for all consumers of said IList<Child> to explicitly invoke IEnumerable<T>.OrderBy() every time they want to enumerate it. Apart from violating DRY, such an approach would also break encapsulation as consumers would have to know that my list is even sorted, which is really none of their business :)
The solution that seemed most logical/efficient was to expose IList<Child> as IEnumerable<Child> (to prevent List mutations) and add explicit Add/Remove methods to the containing Parent. This way, I can intercept changes to the List that necessitate a re-sort, and apply one via Linq:
public class Child {
public string StringProperty;
public int IntProperty;
}
public class Parent{
private IList<Child> _children = new List<Child>();
public IEnumerable<Child> Children{
get
{
return _children;
}
}
private void ReSortChildren(){
_children = new List<Child>(child.OrderBy(c=>c.StringProperty));
}
public void AddChild(Child c){
_children.Add();
ReSortChildren()
}
public void RemoveChild(Child c){
_children.Remove(c);
ReSortChildren()
}
}
Still, this approach doesn't intercept changes made to the underlying Child.StringProperty (which in this case is the property driving the sort). There must be a more elegant solution to such a basic problem, but I haven't been able to find one.
EDIT:
I wasn't clear in that I would preferable a LINQ compatible solution. I'd rather not resort to using .NET 2.0 constructs (i.e. SortedList)
What about using a SortedList<>?
One way you could go about it is to have Child publish an event OnStringPropertyChanged which passes along the previous value of StringProperty. Then create a derivation of SortedList that overrides the Add method to hookup a handler to that event. Whenever the event fires, remove the item from the list and re-add it with the new value of StringProperty. If you can't change Child, then I would make a proxy class that either derives from or wraps Child to implement the event.
If you don't want to do that, I would still use a SortedList, but internally manage the above sorting logic anytime the StringProperty needs to be changed. To be DRY, it's preferable to route all updates to StringProperty through a common method that correctly manages the sorting, rather than accessing the list directly from various places within the class and duplicating the sort management logic.
I would also caution against allowing the controller to pass in a reference to Child, which allows him to manipulate StringProperty after it's added to the list.
public class Parent{
private SortedList<string, Child> _children = new SortedList<string, Child>();
public ReadOnlyCollection<Child> Children{
get { return new ReadOnlyCollection<Child>(_children.Values); }
}
public void AddChild(string stringProperty, int data, Salamandar sal){
_children.Add(stringProperty, new Child(stringProperty, data, sal));
}
public void RemoveChild(string stringProperty){
_children.Remove(stringProperty);
}
private void UpdateChildStringProperty(Child c, string newStringProperty) {
if (c == null) throw new ArgumentNullException("c");
RemoveChild(c);
c.StringProperty = newStringProperty;
AddChild(c);
}
public void CheckSalamandar(string s) {
if (_children.ContainsKey(s))
var c = _children[s];
if (c.Salamandar.IsActive) {
// update StringProperty through our method
UpdateChildStringProperty(c, c.StringProperty.Reverse());
// update other properties directly
c.Number++;
}
}
}
I think that if you derive from KeyedCollection, you'll get what you need. That is only based on reading the documentation, though.
EDIT:
If this works, it won't be easy, unfortunately. Neither the underlying lookup dictionary nor the underlying List in this guy is sorted, nor are they exposed enough such that you'd be able to replace them. It might, however, provide a pattern for you to follow in your own implementation.