aardio 文档

aardio 范例: 自绘小游戏 - 黑洞吞噬

import win.ui;
/*DSG{{*/
var winform = win.form(text="自绘小游戏 - 黑洞吞噬";right=800;bottom=600;bgcolor=0x000000)
winform.add(
gameBox={cls="plus";left=0;top=0;right=800;bottom=600;db=1;dl=1;dr=1;dt=1;notify=1;z=1}
)
/*}}*/

import sys.midiOut;
var midiOut = sys.midiOut();

var game = {
    state = "menu";
    score = 0;
    timeLeft = 90;
    lastTime = 0;
    highScore = 0;
};

var hole = {
    x = 400;
    y = 300;
    targetX = 400;
    targetY = 300;
    radius = 35;
    baseRadius = 35;
    rotation = 0;
    glowIntensity = 1;
    pulsePhase = 0;
    eatPulse = 0;
};

var items = {};
var maxItems = 45;
var particles = {};
var floatingTexts = {};
var itemColors = {
    0xFFe74c3c, 0xFF3498db, 0xFF2ecc71, 0xFFf39c12, 
    0xFF9b59b6, 0xFF1abc9c, 0xFFe91e63, 0xFF00bcd4,
    0xFFff6b6b, 0xFF4ecdc4, 0xFFffe66d, 0xFF95e1d3
};

var stars = {};
var starsInitialized = false;

var initStars = function(width, height) {
    stars = {};

    for(i=1; 120) {
        table.push(stars, {
            x = math.random(0, width);
            y = math.random(0, height);
            size = math.random(1, 2);
            alpha = math.random(30, 100);
            typ = 1;
        });
    }

    for(i=1; 25) {
        table.push(stars, {
            x = math.random(0, width);
            y = math.random(0, height);
            size = math.random(2, 4);
            alpha = math.random(150, 255);
            typ = 2;
        });
    }
    starsInitialized = true;
};

var createParticles = function(x, y, color, count) {
    for(i=1; count) {
        var angle = math.random() * math.pi * 2;
        var speed = math.random(3, 8);
        table.push(particles, {
            x = x; y = y;
            vx = math.cos(angle) * speed;
            vy = math.sin(angle) * speed;
            life = 1;
            decay = math.random(0.02, 0.05);
            size = math.random(3, 8);
            color = color;
        });
    }
};

var createFloatingText = function(x, y, text, color) {
    table.push(floatingTexts, {
        x = x; y = y;
        text = text;
        color = color;
        life = 1;
        vy = -2;
    });
};

var spawnItem = function(rcWidth, rcHeight) {
    var minSize = math.max(10, hole.baseRadius * 0.25);
    var maxSize = math.min(55, hole.baseRadius * 1.3);

    //如果参数 1 大于 参数 2 则 math.random 会报错 'interval is empty'
    var size = minSize<=maxSize ? math.random(minSize, maxSize) : maxSize;

    var x, y;
    var safeDistance = hole.baseRadius + size + 80;
    for(attempt=1; 15) {
        x = math.random(size + 30, rcWidth - size - 30);
        y = math.random(size + 30, rcHeight - size - 30);
        var dx = x - hole.x;
        var dy = y - hole.y;
        if(math.sqrt(dx*dx + dy*dy) > safeDistance) break;
    }

    return {
        x = x; y = y;
        size = size;
        color = itemColors[math.random(1, #itemColors)];
        shape = math.random(1, 5);
        state = "normal";
        absorbProgress = 0;
        rotation = math.random(0, 360);
        rotSpeed = math.random(-3, 3);
        points = math.floor(size * size * 0.5);
        bobPhase = math.random(0, 628) / 100;
    };
};

var initItems = function(rcWidth, rcHeight) {
    items = {};
    particles = {};
    floatingTexts = {};
    for(i=1; maxItems) {
        table.push(items, spawnItem(rcWidth, rcHeight));
    }
};

var resetGame = function(rcWidth, rcHeight) {
    game.score = 0;
    game.timeLeft = 90;
    game.lastTime = time.tick();

    hole.x = rcWidth / 2;
    hole.y = rcHeight / 2;
    hole.targetX = hole.x;
    hole.targetY = hole.y;
    hole.radius = 35;
    hole.baseRadius = 35;
    hole.rotation = 0;
    hole.glowIntensity = 1;
    hole.eatPulse = 0;

    initItems(rcWidth, rcHeight);
};

var boxIntersects = function(item) {
    var margin = 10;
    var r = hole.baseRadius;
    return !(item.x + item.size < hole.x - r - margin || 
             item.x - item.size > hole.x + r + margin || 
             item.y + item.size < hole.y - r - margin || 
             item.y - item.size > hole.y + r + margin);
};

winform.gameBox.onDrawBackground = function(graphics,rc,backgroundColor,foregroundColor){
    var width = rc.right;
    var height = rc.bottom;

    var p1 = ::POINTF(0, 0);
    var p2 = ::POINTF(width, height);
    var bgBrush = gdip.lineBrush(p1, p2, 0xFF080818, 0xFF1a0a2e, 0);
    graphics.fillRectangle(bgBrush, rc);
    bgBrush.delete();

    if(!starsInitialized) {
        initStars(width, height);
    }

    var starBrush = gdip.solidBrush(0xFFFFFFFF);
    for(i=1; #stars) {
        var s = stars[i];
        var color = s.typ == 1 ? 0xFFFFFF : 0xE0E8FF;
        starBrush.color = (s.alpha << 24) | color;
        graphics.fillEllipse(starBrush, s.x, s.y, s.size, s.size);
    }
    starBrush.delete();

    var gridPen = gdip.pen(0x10FFFFFF, 1);
    for(x=0; width; 80) {
        graphics.drawLine(gridPen, x, 0, x, height);
    }
    for(y=0; height; 80) {
        graphics.drawLine(gridPen, 0, y, width, y);
    }
    gridPen.delete();
};

var drawParticles = function(graphics) {
    var brush = gdip.solidBrush(0xFFFFFFFF);
    for(i=1; #particles) {
        var p = particles[i];
        brush.color = (math.floor(p.life * 255) << 24) | (p.color & 0x00FFFFFF);
        graphics.fillEllipse(brush, p.x - p.size/2, p.y - p.size/2, p.size, p.size);
    }
    brush.delete();
};

var drawFloatingTexts = function(graphics) {
    if(#floatingTexts == 0) return;

    var family = gdip.family("Tahoma");
    var font = family.createFont(14, 1, 3);
    var format = gdip.stringformat();
    format.align = 1;

    var measureRect = ::RECTF(0, 0, 200, 100);
    var measured = graphics.measureString("+999", font, measureRect, format);
    var textH = measured.height;

    var brush = gdip.solidBrush(0xFFFFFFFF);
    for(i=1; #floatingTexts) {
        var ft = floatingTexts[i];
        brush.color = (math.floor(ft.life * 255) << 24) | (ft.color & 0x00FFFFFF);
        graphics.drawString(ft.text, font, ::RECTF(ft.x - 50, ft.y, 100, textH + 5), format, brush);
    }

    brush.delete();
    font.delete();
    format.delete();
    family.delete();
};

var drawItem = function(graphics, item) {
    var x = item.x;
    var y = item.y;
    var size = item.size;
    var alpha = 0xFF;

    if(item.state == "normal") {
        y = y + math.sin(item.bobPhase) * 3;
    }
    elseif(item.state == "absorbing") {
        var progress = item.absorbProgress;
        size = item.size * math.max(0.05, 1 - progress * 0.95);
        alpha = math.floor(255 * (1 - progress * 0.8));
        var ease = progress * progress * progress;
        x = item.x + (hole.x - item.x) * ease;
        y = item.y + (hole.y - item.y) * ease;
    }

    var color = (alpha << 24) | (item.color & 0x00FFFFFF);
    var brush = gdip.solidBrush(color);

    if(item.shape == 1) {
        graphics.fillEllipse(brush, x - size, y - size, size * 2, size * 2);
    }
    elseif(item.shape == 2) {
        graphics.translateTransform(x, y);
        graphics.rotateTransform(item.rotation);
        graphics.fillRectangle(brush, ::RECT(-size, -size, size, size));
        graphics.resetTransform();
    }
    elseif(item.shape == 3) {
        var pts = {};
        for(i=0; 2) {
            var angle = item.rotation * math.pi / 180 + i * math.pi * 2 / 3 - math.pi / 2;
            pts[i+1] = ::POINTF(x + size * math.cos(angle), y + size * math.sin(angle));
        }
        graphics.fillPolygon(brush, pts);
    }
    elseif(item.shape == 4) {
        graphics.translateTransform(x, y);
        graphics.rotateTransform(item.rotation);
        var pts = {::POINTF(0, -size), ::POINTF(size*0.7, 0), ::POINTF(0, size), ::POINTF(-size*0.7, 0)};
        graphics.fillPolygon(brush, pts);
        graphics.resetTransform();
    }
    else {
        var pts = {};
        for(i=0; 4) {
            var angle = item.rotation * math.pi / 180 + i * math.pi * 2 / 5 - math.pi / 2;
            pts[i+1] = ::POINTF(x + size * math.cos(angle), y + size * math.sin(angle));
        }
        graphics.fillPolygon(brush, pts);
    }
    brush.delete();
};

var drawHole = function(graphics) {
    var cx = hole.x;
    var cy = hole.y;
    var breathe = 1 + math.sin(hole.pulsePhase) * 0.03;
    var eatEffect = 1 + hole.eatPulse * 0.3;
    var r = hole.baseRadius * breathe * eatEffect;
    hole.radius = r;

    if(hole.eatPulse > 0.5) {
        var waveR = r * (1 + (1 - hole.eatPulse) * 2);
        var wavePen = gdip.pen((math.floor(hole.eatPulse * 100) << 24) | 0xFFFFFF, 2);
        graphics.drawEllipse(wavePen, cx - waveR, cy - waveR, waveR * 2, waveR * 2);
        wavePen.delete();
    }

    var glowPath = gdip.path();
    var glowSize = r * 2 * (1 + hole.eatPulse * 0.5);
    glowPath.addEllipse(cx - glowSize, cy - glowSize, glowSize * 2, glowSize * 2);
    var glowBrush = gdip.pathGradientBrush(glowPath);
    glowBrush.centerColor = 0x00000000;
    glowBrush.surroundColors = {(math.floor(0x30 * hole.glowIntensity) << 24) | 0x9b59b6};
    graphics.fillPath(glowBrush, glowPath);
    glowBrush.delete();
    glowPath.delete();

    var holePath = gdip.path();
    holePath.addEllipse(cx - r, cy - r, r * 2, r * 2);
    var holeBrush = gdip.pathGradientBrush(holePath);
    holeBrush.centerColor = 0xFF000000;
    holeBrush.surroundColors = {0xFF2a1a4a};
    graphics.fillPath(holeBrush, holePath);
    holeBrush.delete();
    holePath.delete();

    var innerPath = gdip.path();
    innerPath.addEllipse(cx - r * 0.6, cy - r * 0.6, r * 1.2, r * 1.2);
    var innerBrush = gdip.pathGradientBrush(innerPath);
    innerBrush.centerColor = 0xFF000000;
    innerBrush.surroundColors = {0xFF0a0a15};
    graphics.fillPath(innerBrush, innerPath);
    innerBrush.delete();
    innerPath.delete();

    var ringAlpha = math.min(0xFF, math.floor((0x90 + hole.eatPulse * 0x60) * hole.glowIntensity));
    var pen1 = gdip.pen((ringAlpha << 24) | 0x9b59b6, 3);
    var pen2 = gdip.pen((ringAlpha << 24) | 0x00d4ff, 2);
    var pen3 = gdip.pen((ringAlpha << 24) | 0xff6b6b, 1.5);

    for(i=0; 2) {
        graphics.drawArc(pen1, cx-r*0.92, cy-r*0.92, r*1.84, r*1.84, hole.rotation+i*120, 50);
    }
    for(i=0; 3) {
        graphics.drawArc(pen2, cx-r*0.75, cy-r*0.75, r*1.5, r*1.5, -hole.rotation*1.3+i*90, 35);
    }
    for(i=0; 5) {
        graphics.drawArc(pen3, cx-r*0.5, cy-r*0.5, r, r, hole.rotation*2+i*60, 20);
    }

    pen1.delete(); pen2.delete(); pen3.delete();
};

var drawUI = function(graphics, rcWidth, rcHeight) {
    var family = gdip.family("Tahoma");
    var font = family.createFont(18, 1, 3);
    var smallFont = family.createFont(12, 0, 3);
    var format = gdip.stringformat();

    var measured = graphics.measureString("⭐ 测试", font, ::RECTF(0,0,500,200), format);
    var fontH = measured.height;
    var smallMeasured = graphics.measureString("最高: 999", smallFont, ::RECTF(0,0,500,200), format);
    var smallH = smallMeasured.height;

    var uiH = fontH + smallH + 16;
    var uiBg = gdip.solidBrush(0x50000000);
    graphics.fillRectangle(uiBg, ::RECT(0, 0, rcWidth, uiH));
    uiBg.delete();

    format.align = 0;
    var scoreBrush = gdip.solidBrush(0xFFf39c12);
    graphics.drawString("⭐ " + game.score, font, ::RECTF(15, 8, 250, fontH+5), format, scoreBrush);
    scoreBrush.delete();

    if(game.highScore > 0) {
        var highBrush = gdip.solidBrush(0x80FFFFFF);
        graphics.drawString("最高: " + game.highScore, smallFont, ::RECTF(15, 8+fontH, 150, smallH+5), format, highBrush);
        highBrush.delete();
    }

    format.align = 2;
    var timeColor = game.timeLeft <= 10 ? 0xFFe74c3c : (game.timeLeft <= 30 ? 0xFFf39c12 : 0xFF2ecc71);
    var timeBrush = gdip.solidBrush(timeColor);
    var timeText = string.format("⏱ %02d:%02d", math.floor(game.timeLeft/60), math.floor(game.timeLeft)%60);
    graphics.drawString(timeText, font, ::RECTF(rcWidth-150, 8, 140, fontH+5), format, timeBrush);
    timeBrush.delete();

    format.align = 1;
    var sizeBrush = gdip.solidBrush(0xFF9b59b6);
    graphics.drawString("◉ " + math.floor(hole.baseRadius), font, ::RECTF(rcWidth/2-80, 8, 160, fontH+5), format, sizeBrush);
    sizeBrush.delete();

    font.delete(); smallFont.delete(); format.delete(); family.delete();
};

var drawMenu = function(graphics, rcWidth, rcHeight) {
    var family = gdip.family("Tahoma");
    var titleFont = family.createFont(42, 1, 3);
    var subFont = family.createFont(16, 0, 3);
    var format = gdip.stringformat();
    format.align = 1;

    var titleH = graphics.measureString("黑洞吞噬", titleFont, ::RECTF(0,0,rcWidth,200), format).height;
    var subH = graphics.measureString("测试", subFont, ::RECTF(0,0,rcWidth,200), format).height;

    var y = rcHeight * 0.25;

    var glowBrush = gdip.solidBrush(0x309b59b6);
    graphics.drawString("黑洞吞噬", titleFont, ::RECTF(3, y+3, rcWidth, titleH+10), format, glowBrush);
    glowBrush.delete();

    var titleBrush = gdip.solidBrush(0xFFFFFFFF);
    graphics.drawString("黑洞吞噬", titleFont, ::RECTF(0, y, rcWidth, titleH+10), format, titleBrush);
    titleBrush.delete();

    y = y + titleH + 15;
    var subBrush = gdip.solidBrush(0xFF9b59b6);
    graphics.drawString("BLACK HOLE", subFont, ::RECTF(0, y, rcWidth, subH+5), format, subBrush);
    subBrush.delete();

    y = y + subH * 2.5;
    var tipBrush = gdip.solidBrush(0xAAFFFFFF);
    graphics.drawString("移动鼠标控制黑洞", subFont, ::RECTF(0, y, rcWidth, subH+5), format, tipBrush);
    y = y + subH * 1.4;
    graphics.drawString("吞噬比你小的物体来成长壮大", subFont, ::RECTF(0, y, rcWidth, subH+5), format, tipBrush);
    tipBrush.delete();

    y = y + subH * 2.5;
    var blink = math.floor(180 + math.sin(time.tick()/300) * 75);
    var startBrush = gdip.solidBrush((blink << 24) | 0x2ecc71);
    graphics.drawString("[ 单击开始游戏 ]", subFont, ::RECTF(0, y, rcWidth, subH+5), format, startBrush);
    startBrush.delete();

    if(game.highScore > 0) {
        y = y + subH * 2;
        var highBrush = gdip.solidBrush(0xFFf39c12);
        graphics.drawString("最高分: " + game.highScore, subFont, ::RECTF(0, y, rcWidth, subH+5), format, highBrush);
        highBrush.delete();
    }

    titleFont.delete(); subFont.delete(); format.delete(); family.delete();
};

var drawGameOver = function(graphics, rcWidth, rcHeight) {
    var maskBrush = gdip.solidBrush(0x90000000);
    graphics.fillRectangle(maskBrush, ::RECT(0, 0, rcWidth, rcHeight));
    maskBrush.delete();

    var family = gdip.family("Tahoma");
    var titleFont = family.createFont(36, 1, 3);
    var scoreFont = family.createFont(48, 1, 3);
    var subFont = family.createFont(18, 0, 3);
    var format = gdip.stringformat();
    format.align = 1;

    var titleH = graphics.measureString("时间到", titleFont, ::RECTF(0,0,rcWidth,200), format).height;
    var scoreH = graphics.measureString("999", scoreFont, ::RECTF(0,0,rcWidth,200), format).height;
    var subH = graphics.measureString("测试", subFont, ::RECTF(0,0,rcWidth,200), format).height;

    var y = rcHeight * 0.22;

    var titleBrush = gdip.solidBrush(0xFFe74c3c);
    graphics.drawString("时间到!", titleFont, ::RECTF(0, y, rcWidth, titleH+10), format, titleBrush);
    titleBrush.delete();

    y = y + titleH + 20;
    var scoreBrush = gdip.solidBrush(0xFFf39c12);
    graphics.drawString(tostring(game.score), scoreFont, ::RECTF(0, y, rcWidth, scoreH+10), format, scoreBrush);
    scoreBrush.delete();

    y = y + scoreH + 10;
    var labelBrush = gdip.solidBrush(0xAAFFFFFF);
    graphics.drawString("最终得分", subFont, ::RECTF(0, y, rcWidth, subH+5), format, labelBrush);
    labelBrush.delete();

    y = y + subH * 1.5;
    if(game.score >= game.highScore && game.score > 0) {
        var newBrush = gdip.solidBrush(0xFF2ecc71);
        graphics.drawString("✨ 新纪录!", subFont, ::RECTF(0, y, rcWidth, subH+5), format, newBrush);
        newBrush.delete();
        y = y + subH * 1.5;
    }

    y = y + subH * 0.5;
    var blink = math.floor(180 + math.sin(time.tick()/300) * 75);
    var tipBrush = gdip.solidBrush((blink << 24) | 0xFFFFFF);
    graphics.drawString("[ 单击重新开始 ]", subFont, ::RECTF(0, y, rcWidth, subH+5), format, tipBrush);
    tipBrush.delete();

    titleFont.delete(); scoreFont.delete(); subFont.delete(); format.delete(); family.delete();
};

winform.gameBox.onDrawContent = function(graphics,rc,txtColor,rcContent,foregroundColor,font){
    graphics.smoothingMode = 4;
    var rcWidth = rc.right;
    var rcHeight = rc.bottom;

    if(game.state == "menu") {
        hole.x = rcWidth / 2;
        hole.y = rcHeight * 0.85;
        hole.baseRadius = 50;
        drawHole(graphics);
        drawMenu(graphics, rcWidth, rcHeight);
        return;
    }

    for(i=1; #items) {
        if(items[i].state == "normal") drawItem(graphics, items[i]);
    }

    drawParticles(graphics);

    for(i=1; #items) {
        if(items[i].state == "absorbing") drawItem(graphics, items[i]);
    }

    drawHole(graphics);
    drawFloatingTexts(graphics);

    if(game.state == "playing") drawUI(graphics, rcWidth, rcHeight);
    if(game.state == "gameover") drawGameOver(graphics, rcWidth, rcHeight);
};

/*
在 onMouseMove 里密集重绘是不必要的而且对性能影响较大。
一种方法是用另一个 plus 控件调用 orphanWindow(true) 方法创建悬浮透明窗口模拟拖动物体(参考拼图游戏范例)。
另一种方法是让动画定时器负责更新画面,当前游戏就是这么做的。
*/ 
winform.gameBox.onMouseMove = function(wParam, lParam) {
    var x, y = win.getMessagePos(lParam);
    if(game.state == "playing") {
        hole.targetX = x;
        hole.targetY = y;
    }
};


winform.gameBox.onMouseUp = function(wParam, lParam) {
    var rc = owner.getClientRect();
    if(game.state == "menu" || game.state == "gameover") {
        if(game.score > game.highScore) game.highScore = game.score;
        game.state = "playing";
        resetGame(rc.right, rc.bottom);
        if(midiOut) midiOut.play("changeInstrument(10), 1_,3_,5_,1'__", "C4", 100);
        winform.gameBox.cursor = ::User32.LoadCursor(,32515/*_IDC_CROSS*/);
    } 
};

winform.gameBox.onAnimation = function(state) {
    var rc = owner.getClientRect();
    var rcWidth = rc.right;
    var rcHeight = rc.bottom;

    hole.rotation = (hole.rotation + 3) % 360;
    hole.pulsePhase = hole.pulsePhase + 0.08;
    if(hole.glowIntensity > 1) hole.glowIntensity = math.max(1, hole.glowIntensity - 0.02);
    if(hole.eatPulse > 0) hole.eatPulse = math.max(0, hole.eatPulse - 0.08);

    for(i=#particles; 1; -1) {
        var p = particles[i];
        p.x = p.x + p.vx; p.y = p.y + p.vy;
        p.life = p.life - p.decay;
        if(p.life <= 0) table.remove(particles, i);
    }

    for(i=#floatingTexts; 1; -1) {
        var ft = floatingTexts[i];
        ft.y = ft.y + ft.vy;
        ft.life = ft.life - 0.02;
        if(ft.life <= 0) table.remove(floatingTexts, i);
    }

    if(game.state != "playing") return true;

    var now = time.tick();
    if(game.lastTime == 0) game.lastTime = now;
    game.timeLeft = game.timeLeft - (now - game.lastTime) / 1000;
    game.lastTime = now;

    if(game.timeLeft <= 0) {
        game.timeLeft = 0;
        game.state = "gameover";
        if(game.score > game.highScore) game.highScore = game.score;
        if(midiOut) midiOut.playAsync("changeInstrument(47), '5_,'4_,'3_,'2_,'1__", "C3", 180);
        winform.gameBox.cursor = ::User32.LoadCursor(,32512/*_IDC_ARROW*/);
        return true;
    }

    //带阻尼的弹性感,系数越大跟随鼠标的速度越快
    hole.x = hole.x + (hole.targetX - hole.x) * 0.60;
    hole.y = hole.y + (hole.targetY - hole.y) * 0.60;

    hole.x = math.max(hole.baseRadius, math.min(rcWidth - hole.baseRadius, hole.x));
    hole.y = math.max(hole.baseRadius, math.min(rcHeight - hole.baseRadius, hole.y));

    var toRemove = {};
    var absorbed = 0;

    for(i=1; #items) {
        var item = items[i];
        if(item.state == "absorbing") {
            item.absorbProgress = item.absorbProgress + 0.1;
            item.rotation = item.rotation + 15;
            if(item.absorbProgress >= 1) {
                table.push(toRemove, i);
                game.score = game.score + item.points;
                hole.eatPulse = math.min(1, hole.eatPulse + 0.6);
                hole.glowIntensity = math.min(2.5, hole.glowIntensity + 0.5);
                hole.baseRadius = hole.baseRadius + item.size * (0.04 + item.size/hole.baseRadius * 0.02);
                createParticles(hole.x, hole.y, item.color, 12);
                createFloatingText(hole.x, hole.y - hole.radius - 20, "+" + item.points, 0xFFFFFF);
                absorbed = absorbed + 1;
            }
        }
        elseif(boxIntersects(item)) {
            var dx = item.x - hole.x;
            var dy = item.y - hole.y;
            if(math.sqrt(dx*dx + dy*dy) < hole.baseRadius * 0.8 && item.size <= hole.baseRadius * 0.75) {
                item.state = "absorbing";
                item.absorbProgress = 0;
                hole.eatPulse = math.min(1, hole.eatPulse + 0.2);
                if(midiOut) {
                    var pitch = item.size < 20 ? "C5" : (item.size < 35 ? "C4" : "C3");
                    midiOut.playAsync("changeInstrument(115), 5_,3_", pitch, 80);
                }
            }
        }
    }

    if(absorbed >= 2 && midiOut) midiOut.playAsync("changeInstrument(10), 5_,1'_,3'_", "C5", 60);

    for(i=#toRemove; 1; -1) {
        table.remove(items, toRemove[i]);
        table.push(items, spawnItem(rcWidth, rcHeight));
    }

    for(i=1; #items) {
        var item = items[i];
        if(item.state == "normal") {
            item.rotation = item.rotation + item.rotSpeed;
            item.bobPhase = item.bobPhase + 0.05;
        }
    }

    return true;
};

//人眼就会感觉流畅即可,过多而不必要的密集绘制反而会起到副作用
winform.gameBox.startAnimation(22);

winform.show();
win.loopMessage();
Markdown 格式