mvcExpress live beta released

Hi,

mvcExpress live beta is out!

mvcExpress live is designed to allow creating real time MVC architecture application in flash, like action games, or anything that needs rapid continuous code execution, rendering, animating, even just as3 code batching.

It adds to MVC new actor that I call for now – Engine. This actor deals with two new class types : Process and Task.

Here is short overview of what mvcExpress live is all about:

mvcExpress live is extension of mvcExpress, than means that you still have Proxies to handle data Model, Mediators to handle View, and Commands for Controller. But additionally you have Processes and Tasks for Engine.

You start setting-up you engine by executing Command that will map and most likely start a Process. You can map and start enterFrame based process or Timer based process. For frame based process you can optionally define frame skipping or leave it at 0 to make process tick every frame, for timer based process you define delay beet-wean process ticks.

Example of command:

[as3 gutter=”1″] package controller {
import engine.TestEnterFrameProcess;
import engine.TestTimerProcess;
import org.mvcexpress.mvc.Command;

public class SetUpEngineCommand extends Command {

public function execute(blank:Object):void {

processMap.mapFrameProcess(TestEnterFrameProcess, 10, "optionalName");
processMap.startProcess(TestEnterFrameProcess, "optionalName");

processMap.mapFrameProcess(TestTimerProcess, 1000, "optionalTimerName");
processMap.startProcess(TestTimerProcess, "optionalTimerName");

}

}
}
[/as3]

If you want to use enterFrame based processes you must set once application stage to processMap, best place to do it is module class:

[as3] processMap.setStage(main.stage);
[/as3]

You can also processMap.unmapProcess(…); and processMap.stopProcess(…); in your commands.
You can’t get Process object directly – it is decoupled from commands.

Then Process is mapped – it’s onRegister() function is triggered, this is good place to initiate you process, add Tasks and add message handlers.
In fact processes are very similar to mediators: they can inject proxies, handle messages, but instead of mediating the view they handle tasks. Process also can’t send messages directly, but this is something tasks do. Example of process class:

[as3 gutter=”1″] package engine {
import org.mvcexpress.live.Process;
import engine.tasks.*

public class TestTimerProcess extends Process {

override protected function onRegister():void {

addFirstTask(MoveHeroTask);

addTask(MoveEnemiesTask);
addTask(CollideHeroWithWallsTask);
addTask(CollideHeroWithEnemiesTask);
addTask(RenderProjectilesTask);
addTask(RenderPlayFieldTask)

addTaskAfter(RenderFieldObjectsTask, RenderPlayFieldTask);

addHandler(Message.PARALIZE_HERO, handleHeroParalize);
addHandler(Message.UNPARALIZE_HERO, handleHeroUnparalize);

addHandler(Message.ADD_MINIMAP, handleMinimapAdded);
addHandler(Message.REMOVE_MINIMAP, handleMinimapRemoved);

addHandler(Message.STOP_TEST_PROCESS, handleStopProcess);
addHandler(Message.START_TEST_PROCESS, handleStartProcess);

}

private function handleHeroParalize(blank:Object):void {
disableTask(MoveHeroTask);
}

private function handleHeroUnparalize(blank:Object):void {
enableTask(MoveHeroTask);
}

private function handleMinimapAdded():void {
addTask(RenderMinimapTask);
}

private function handleMinimapRemoved():void {
removeTask(RenderMinimapTask);
}

private function handleStartProcess():void {
startProcess();
}

private function handleStopProcess():void {
stopProcess();
}

}
}
[/as3]

So process manages tasks, but can’t access them directly. If Process is running – every tick it runs all tasks that are enabled and has all dependencies injected. (if task is missing dependency – it is automatically and silently skipped.)

If you get into situation there task is not running… and you don’t know what is happening.. you have to check if it is enabled, and if all dependencies are provided. Best way to do it is by using mvcExrpess logger.

Before talking about injecting objects into tasks let’s talk how to provide process with objects to use for injections.

Basically what we want tasks to do – is to handle data objects and view objects quickly. (Something that commands can’t do.) But data objects are encapsulated in proxies, and view objects are encapsulated in Mediators. We will need to break that encapsulation! Both Proxies and Mediators gets 2 new functions: provide() and unprovide().

You must name you provided objects with unique string name, and you can’t provide primitive types. Example of proxy and mediator that provides some objects:

[as3 gutter=”1″] package model {
import constants.ProvideIds;
import flash.geom.Point;
import org.mvcexpress.mvc.Proxy;

public class HeroProxy extends Proxy {

private var position:Point = new Point();

private var heroAttribs:HeroAtribsVO = new HeroAtribsVO();

override protected function onRegister():void {

provide(position, "hero_position");

privide(heroAttribs, "hero_attributes");
}

}
}
[/as3]

 

[as3 gutter=”1″] package view {
import org.mvcexpress.mvc.Mediator;

public class HeroMediator extends Mediator {

[Inject] public var view:Hero;

override public function onRegister():void {
provide(view, "hero_view");
}

}
}
[/as3]

Then objects are provided they are injected into tasks by using [Inject(name=”hero_position”)] metadata tag.
If Task have inject that is not provided – task will be silently skipped and not run then process ticks, but as soon as you provide needed object – it will start to run.

Lets talk about Task class. Task encapsulates work that has to be done with data or view object repeatedly and rapidly. This work is done in run() function. This function is executed every process tick, unless task is disabled by user or has missing injections.

Lets see example of the task:

[as3 gutter=”1″] package engine.tasks {
import flash.geom.Point;
import model.HeroAtribsVO;
import org.mvcexpress.live.Task;
import view.Hero;

public class RenderHero extends Task {

[Inject(name="hero_position")] public var heroPosition:Point;

[Inject(name="hero_attributes")] public var heroAttribs:HeroAtribsVO;

[Inject(name="hero_view")] public var heroView:Hero;

override public function run():void {
heroPosition.x += heroAttribs.moveVectorX;
heroPosition.y += heroAttribs.moveVectorY;

if (heroPosition.y < GameConstants.GROUND_Y) {
heroPosition.y = GameConstants.GROUND_Y;
heroAttribs.moveVectorY = 0;
sendPostMessage(Message.HERO_HIT_GROUND);
}

hero.x = heroPosition.x;
hero.y = heroPosition.y;
}

[Test(delay="5000")]

CONFIG::debug
public function hero_positon_equals() {
assert.equals(heroView.x, heroPosition.x, "Hero x position is damaged. View and data difers.");
assert.equals(heroView.y, heroPosition.y, "Hero y position is damaged. View and data difers.");
}
}
}
[/as3]

This task moves hero by his movement direction vector values, updates hero view object position, check collision with the ground and does it every process tick.

This is only example, in practice you should try to do one thing only in the task. for example – only move hero, or collide.

Then hero hits the ground this task sends a message about it, so Command on Mediator can react to it. Tasks have 3 different functions to send messages sendInstantMessage() will send message right now, and it works the same as standard sendMessage() function from Commands, Mediators or Proxies. sendPostMessage() will send message after current task run(), and this is most convenient way sending messages from tasks.  sendFinalMessage() will send message after all current process tasks are done running;

But that’s not all. It also has test function. Then you mix data and view objects you lose one very important MVC benefit – view and data separation. This can lead to control loss. To get this control back mvcExpress live uses unit test like test functions.

Test functions must have [Test] metadata tag, delay attribute can be used to define how often test function must be run, or optional count attribute to define how much times test function must be repeated(this helps if you have too much data to test and want to sample subset of that data). Test function should test things from what task does, it sounds counter intuitive – why would you test something that wan just set in run() function, but all test are run after process ends it’s current tick, so it  tests if your application is in proper state after all tasks are run.

This is very simple example, but you can do a lot of different things in tasks… collide with objects.. update data by checking keyboard presses, change starling animation frames, move 3d objects, and so on.

You can download mvcExpress live beta from gitHub.

Prototype project : Ludum Dare #25 game jam entry : https://github.com/MindScriptAct/derils-gameJam-ludum-dare-25

Have fun!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>