I am drawing a cellular automaton simulation on a Flex Canvas. I alternate computing the state of the automaton with updating the graphics on the Panel. However, the updating of the Panel does not seem to be "keeping up" with the update of the CA state.
I'm wondering whether I am doing something wrong with my handling of the graphics code. The code is below. Sorry it's so lengthy, but I don't think the problem will happen with anything simpler (well, it probably will, but I'm not certain I want to take the time to find it).
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init();">
<mx:Script>
<![CDATA[
private static const universeRadius:uint = 8;
private var universes:Array = new Array();
private var currentUniverse:uint = 0;
private var squareHeight:uint;
private var squareWidth:uint;
private function init():void {
squareHeight = myCanvas.height / universeRadius;
squareWidth = myCanvas.width / universeRadius;
initUniverses();
while (true) {
trace("Calling draw()");
draw();
trace("Calling updateUniverse()");
updateUniverse();
}
}
private function initUniverses():void {
var universe0:Array = new Array();
开发者_如何学Python var universe1:Array = new Array();
var i:int;
for (i = 0; i < universeRadius; i++) {
var universe0Row:Array = new Array();
var universe1Row:Array = new Array();
var j:int;
for (j = 0; j < universeRadius; j++) {
if (Math.random() < 0.5) {
universe0Row[j] = 0;
} else {
universe0Row[j] = 1;
}
universe1Row[j] = 0;
}
universe0[i] = universe0Row;
universe1[i] = universe1Row;
}
universes[0] = universe0;
universes[1] = universe1;
}
private function normalize(pos:int):int {
return (pos + universeRadius) % universeRadius;
}
private function updateUniverse():void {
var newUniverse:int = 1 - currentUniverse;
var i:int;
for (i = 0; i < universeRadius; i++) {
var j:int;
for (j = 0; j < universeRadius; j++) {
var dx:int;
var dy:int;
var count:int = 0;
for (dx = -1; dx <= 1; dx++) {
var neighborX:int = normalize(i + dx);
for (dy = -1; dy <= 1; dy++) {
var neighborY:int = normalize(j + dy);
if ((dx != 0 || dy != 0) &&
universes[currentUniverse][neighborX][neighborY] == 1) {
count++;
}
}
}
var currentCell:int = universes[currentUniverse][i][j];
if (currentCell == 1) {
// 1. Any live cell with fewer than two live neighbours
// dies, as if caused by underpopulation.
if (count < 2) {
universes[newUniverse][i][j] = 0;
}
// 2. Any live cell with more than three live neighbours
// dies, as if by overcrowding.
else if (count > 3) {
universes[newUniverse][i][j] = 0;
}
// 3. Any live cell with two or three live neighbours
// lives on to the next generation.
else {
universes[newUniverse][i][j] = 1;
}
} else {
// 4. Any dead cell with exactly three live neighbours
// becomes a live cell.
if (count == 3) {
universes[newUniverse][i][j] = 1;
} else {
universes[newUniverse][i][j] = 0;
}
}
}
}
currentUniverse = newUniverse;
}
private function draw():void {
myCanvas.graphics.clear();
myCanvas.graphics.beginFill(0xFFFFFF, 1.0);
myCanvas.graphics.drawRect(0, 0, myCanvas.width, myCanvas.height);
var i:int;
for (i = 0; i < universeRadius; i++) {
var j:int;
for (j = 0; j < universeRadius; j++) {
if (universes[currentUniverse][i][j] == "1") {
myCanvas.graphics.beginFill(0x000000, 1.0);
myCanvas.graphics.drawRect(
j * squareWidth, i * squareHeight, squareWidth, squareHeight)
}
}
}
myCanvas.graphics.endFill();
}
]]>
</mx:Script>
<mx:Panel title="Life" height="95%" width="95%"
paddingTop="5" paddingLeft="5" paddingRight="5" paddingBottom="5">
<mx:Canvas id="myCanvas" borderStyle="solid" height="100%" width="100%">
</mx:Canvas>
</mx:Panel>
</mx:Application>
Scary:
while (true) {
Flex is single-threaded, so I think you're never giving the UIComponents a chance to fully update.
Alternatives would be to run the draw and update methods on a Timer, or run them on the ENTER_FRAME
event. Either of these would let the display list updates occur in between your
private function cycleUniverse(event:Event):void {
updateUniverse();
draw();
}
and then either of
private var t:Timer;
private function init():void {
t = new Timer(500); // every 500 ms
t.addEventListener(TimerEvent.Timer, cycleUniverse);
t.start();
}
or
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" enterFrame="cycleUniverse(event)">
精彩评论