利用aardio制作简单的泡泡龙游戏

Mr_MAO 18天前 250

import win.ui;
import gdip;
/*DSG{{*/
var winform = win.form(text="泡泡龙游戏 (By Mr_MAO)";right=480;bottom=640;bgcolor=0xFFFFFF;border="dialog frame";max=false)
winform.add(
canvas={cls="plus";left=0;top=0;right=480;bottom=640;db=1;dl=1;dr=1;dt=1;notify=1;z=1}
)
/*}}*/

// --- 游戏常量 ---
var RADIUS = 20;            // 泡泡半径
var DIAMETER = RADIUS * 2;
var ROW_HEIGHT = RADIUS * math.sqrt(3); // 六边形行高
var COLS = 11;              // 列数 (偶数行11个, 奇数行10个)
var ROWS = 15;              // 网格最大行数
var BOARD_WIDTH = COLS * DIAMETER; 

// 颜色定义
var colors = {
    0xFFFF0000; // 红
    0xFF00FF00; // 绿
    0xFF0000FF; // 蓝
    0xFFFFFF00; // 黄
    0xFFFF00FF; // 紫
    0xFF00FFFF; // 青
};

// --- 游戏状态 ---
var grid = {};       // 网格二维数组 grid[row][col] = colorIndex
var bullet = null;   // 当前飞行的泡泡 {x, y, dx, dy, color}
var nextColor = 0;   // 下一个发射的颜色
var currColor = 0;   // 当前发射的颜色
var angle = -90;     // 发射角度
var isGameOver = false;

// --- 工具函数:获取网格坐标对应的屏幕像素位置 ---
var getPixelPos = function(r, c){
    var x = c * DIAMETER + RADIUS;
    // 奇数行向右偏移半个身位
    if(r % 2 != 0){
        x = x + RADIUS;
    }
    var y = r * ROW_HEIGHT + RADIUS;
    return x, y;
}

// --- 工具函数:计算两个圆心的距离平方 ---
var getDistSq = function(x1, y1, x2, y2){
    return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
}

// --- 初始化游戏 ---
function initGame(){
    grid = {};
    isGameOver = false;
    
    // 初始化网格,生成几行初始泡泡
    for(r=0; 5){ 
        grid[r] = {};
        // 偶数行COLS个,奇数行COLS-1个
        var count = (r % 2 == 0) ? COLS : COLS - 1;
        for(c=0; count-1){
            grid[r][c] = math.random(1, #colors);
        }
    }
    
    currColor = math.random(1, #colors);
    nextColor = math.random(1, #colors);
    bullet = null;
    winform.canvas.redraw();
}

// --- 查找相连的同色泡泡 (递归/DFS) ---
findCluster = function(r, c, matchColor, foundList, visited){
    var key = r + "," + c;
    if(visited[key]) return;
    
    // 边界检查
    if(!grid[r] || !grid[r][c]) return;
    if(grid[r][c] != matchColor) return;
    
    visited[key] = true;
    table.push(foundList, {r=r; c=c});
    
    // 六个方向的邻居偏移量 (偶数行和奇数行不同)
    var offsets = (r % 2 == 0) 
        ? {{r-1,c-1}, {r-1,c}, {r,c-1}, {r,c+1}, {r+1,c-1}, {r+1,c}}  // 偶数行邻居
        : {{r-1,c}, {r-1,c+1}, {r,c-1}, {r,c+1}, {r+1,c}, {r+1,c+1}}; // 奇数行邻居
        
    for(i=1; #offsets){
        var nr, nc = offsets[i][1], offsets[i][2];
        findCluster(nr, nc, matchColor, foundList, visited);
    }
}

// --- 查找根节点 (用于悬空检测) ---
var findFloating = function(){
    var visited = {};
    var queue = {};
    
    // 1. 将第一行的所有泡泡加入队列
    if(grid[0]){
        for(c, color in grid[0]){
            table.push(queue, {r=0; c=c});
            visited["0," + c] = true;
        }
    }
    
    // 2. BFS 遍历所有连接到顶部的泡泡
    var head = 1;
    while(head <= #queue){
        var curr = queue[head];
        var r, c = curr.r, curr.c;
        head++;
        
        var offsets = (r % 2 == 0) 
            ? {{r-1,c-1}, {r-1,c}, {r,c-1}, {r,c+1}, {r+1,c-1}, {r+1,c}}
            : {{r-1,c}, {r-1,c+1}, {r,c-1}, {r,c+1}, {r+1,c}, {r+1,c+1}};
            
        for(i=1; #offsets){
            var nr, nc = offsets[i][1], offsets[i][2];
            if(grid[nr] && grid[nr][nc]){
                var key = nr + "," + nc;
                if(!visited[key]){
                    visited[key] = true;
                    table.push(queue, {r=nr; c=nc});
                }
            }
        }
    }
    
    // 3. 移除未被访问到的泡泡 (即悬空的)
    for(r, row in grid){
        for(c, color in row){
            if(!visited[r + "," + c]){
                grid[r][c] = null; // 移除
            }
        }
    }
}

// --- 处理碰撞后的逻辑 ---
function snapBubble(){
    // 简单的碰撞处理:找到距离子弹最近的一个空网格位置
    var minDist = 999999;
    var bestR, bestC = -1, -1;
    
    // 遍历所有可能的网格位置(稍微有些低效但逻辑简单)
    for(r=0; ROWS){
        var cols = (r % 2 == 0) ? COLS : COLS - 1;
        for(c=0; cols-1){
            if( (!grid[r] || !grid[r][c]) ){ // 必须是空格
                var px, py = getPixelPos(r, c);
                var dist = getDistSq(bullet.x, bullet.y, px, py);
                if(dist < minDist){
                    minDist = dist;
                    bestR, bestC = r, c;
                }
            }
        }
    }
    
    // 只有当距离足够近时才吸附 (防止吸附到太远的地方)
    if(bestR != -1 && minDist < DIAMETER*DIAMETER){
        if(!grid[bestR]) grid[bestR] = {};
        grid[bestR][bestC] = bullet.color;
        
        // 1. 检查消除
        var matches = {};
        findCluster(bestR, bestC, bullet.color, matches, {});
        
        if(#matches >= 3){
            // 消除
            for(i=1; #matches){
                var m = matches[i];
                grid[m.r][m.c] = null;
            }
            // 2. 检查悬空并掉落
            findFloating();
        }
        
        // 检查游戏结束 (泡泡到底部)
        if(bestR >= ROWS - 2) isGameOver = true;
    }
    
    bullet = null; // 销毁子弹
    currColor = nextColor;
    nextColor = math.random(1, #colors);
    winform.canvas.redraw();
}

// --- 游戏主循环 (更新逻辑) ---
function update(){
    if(isGameOver || !bullet) return;
    
    // 移动子弹
    bullet.x = bullet.x + bullet.dx;
    bullet.y = bullet.y + bullet.dy;
    
    // 墙壁反弹
    if(bullet.x <= RADIUS || bullet.x >= winform.canvas.width - RADIUS){
        bullet.dx = -bullet.dx;
        bullet.x = bullet.x + bullet.dx; // 推出墙壁
    }
    
    // 顶部碰撞
    if(bullet.y <= RADIUS){
        snapBubble();
        return;
    }
    
    // 泡泡碰撞检测
    var hit = false;
    for(r, row in grid){
        for(c, col in row){
            if(col){
                var px, py = getPixelPos(r, c);
                // 如果距离小于直径,说明碰撞
                if(getDistSq(bullet.x, bullet.y, px, py) < DIAMETER * DIAMETER * 0.8){ 
                    // *0.8 是为了让碰撞判定稍微宽松/紧凑一点
                    hit = true;
                    break; 
                }
            }
        }
        if(hit) break;
    }
    
    if(hit){
        snapBubble();
    }
    
    winform.canvas.redraw();
}

// --- 绘图 ---
winform.canvas.onDrawForegroundEnd  = function(graphics, rc){
    graphics.clear(0xFF333333);
    graphics.smoothingMode = 4;
    
    // 绘制网格中的泡泡
    for(r=0; ROWS){
        if(grid[r]){
            for(c, colorIdx in grid[r]){
                if(colorIdx){
                    var x, y = getPixelPos(r, c);
                    var brush = gdip.solidBrush(colors[colorIdx]);
                    graphics.fillEllipse(brush, x-RADIUS, y-RADIUS, DIAMETER, DIAMETER);
                    brush.delete();
                    
                    // 高光
                    var brushW = gdip.solidBrush(0x50FFFFFF);
                    graphics.fillEllipse(brushW, x-RADIUS+5, y-RADIUS+5, 10, 10);
                    brushW.delete();
                }
            }
        }
    }
    
    // 绘制发射台 (底部中间)
    var shooterX = rc.width / 2;
    var shooterY = rc.height - 50;
    
    // 绘制箭头
    var penArrow = gdip.pen(0xFFFFFFFF, 3);
    var endX = shooterX + math.cos(angle * math.pi / 180) * 60;
    var endY = shooterY + math.sin(angle * math.pi / 180) * 60;
    graphics.drawLine(penArrow, shooterX, shooterY, endX, endY);
    penArrow.delete();
    
    // 绘制当前待发射泡泡
    if(!bullet){
        var brushCurr = gdip.solidBrush(colors[currColor]);
        graphics.fillEllipse(brushCurr, shooterX-RADIUS, shooterY-RADIUS, DIAMETER, DIAMETER);
        brushCurr.delete();
    } else {
        // 绘制飞行中的泡泡
        var brushFly = gdip.solidBrush(colors[bullet.color]);
        graphics.fillEllipse(brushFly, bullet.x-RADIUS, bullet.y-RADIUS, DIAMETER, DIAMETER);
        brushFly.delete();
    }
    
    // 绘制下一个预览泡泡
    var brushNext = gdip.solidBrush(colors[nextColor]);
    graphics.fillEllipse(brushNext, shooterX + 80, shooterY, RADIUS, RADIUS); // 小一点
    brushNext.delete();

    var fontFamily = gdip.family("Arial");
    var font = fontFamily.createFont(10, 1); 
    var strformat = gdip.stringformat();
    graphics.drawString("NEXT", font, ::RECTF(shooterX + 80, shooterY + 20,1000,1000), strformat, gdip.solidBrush(0xFFFFFFFF));
    
    if(isGameOver){
    	var fontBig = fontFamily.createFont(30, 1); 
        graphics.drawString("GAME OVER", fontBig, ::RECTF(100, 300,1000,1000), strformat, gdip.solidBrush(0xFFFF0000));
        graphics.drawString("点击重启", font, ::RECTF(180, 350,1000,1000), strformat, gdip.solidBrush(0xFFFFFFFF));
        fontBig.delete();
    }
    font.delete();
}

// --- 鼠标移动 (瞄准) ---
winform.canvas.onMouseMove = function(wParam, lParam){
    if(isGameOver) return;
    var x, y = win.getMessagePos(lParam);
    var shooterX = winform.canvas.width / 2;
    var shooterY = winform.canvas.height - 50;
    
    // 计算角度 (atan2)
    var dx = x - shooterX;
    var dy = y - shooterY;
    
    // 限制角度,防止射向地板
    if(dy >= 0) dy = -1; 
    
    angle = math.atan2(dy, dx) * 180 / math.pi;
    winform.canvas.redraw();
}

// --- 鼠标点击 (发射) ---
winform.canvas.onMouseDown = function(wParam, lParam){
    if(isGameOver){
        initGame();
        return;
    }
    
    if(bullet) return; // 已有子弹在飞,不能发射
    
    var shooterX = winform.canvas.width / 2;
    var shooterY = winform.canvas.height - 50;
    var speed = 15;
    
    bullet = {
        x = shooterX;
        y = shooterY;
        dx = math.cos(angle * math.pi / 180) * speed;
        dy = math.sin(angle * math.pi / 180) * speed;
        color = currColor;
    };
}

// --- 定时器 (60FPS) ---
var tmr = winform.setInterval(
    16, 
    function(){
        update();
    }
)

// 调整窗口大小以适应网格宽度
var width = COLS * DIAMETER + RADIUS; // 加上左右边距
winform.width = width;
winform.canvas.right = width;

initGame();

winform.enableDpiScaling(false);
winform.show();
win.loopMessage();


最新回复 (3)
  • 光庆 15天前
    0 2
    厉害厉害,学到了
  • netfox 14天前
    0 3
    光庆 厉害厉害,学到了
    严重怀疑大佬,在恶意顶贴
  • Mr_MAO 2天前
    0 4

    今天抽空优化了一下,让这个小游戏也具有可玩性!

    import win.ui;
    import gdip;
    /*DSG{{*/
    var winform = win.form(text="泡泡龙小游戏v2 (By Mr_MAO)";right=480;bottom=640;bgcolor=0x333333;border="dialog frame";max=false)
    winform.add(
    canvas={cls="plus";left=0;top=0;right=480;bottom=640;db=1;dl=1;dr=1;dt=1;notify=1;z=1}
    )
    /*}}*/
    
    // --- 游戏配置 ---
    var RADIUS = 20;            
    var DIAMETER = RADIUS * 2;
    var ROW_HEIGHT = RADIUS * math.sqrt(3); 
    var COLS = 12;              
    var ROWS = 18;              
    var ADD_ROW_INTERVAL = 10000;        
    var MAX_BUBBLE_ROWS = 15;  // 第15行为失败判定线         
    var INITIAL_ROWS = 3;               
    var GAME_DURATION = 180000;         
    var FADE_SPEED = 12;               
    
    var colors = { 0xFFFF4444; 0xFF44FF44; 0xFF4444FF; 0xFFFFFF44; 0xFFFF44FF; 0xFF44FFFF };
    
    // --- 预创建绘图资源 ---
    var gFamily = gdip.family("Arial");
    var gFontMain = gFamily.createFont(10, 1);
    var gFontBig = gFamily.createFont(35, 1);
    var gStrFormat = gdip.stringformat();
    gStrFormat.align = 1/*_GdipStringAlignmentCenter*/; // 居中对齐
    
    // --- 游戏状态 ---
    var grid = {};               
    var gridOffsetRows = 0;      
    var bullet = null;           
    var nextColor, currColor = 0, 0;           
    var angle = -90;             
    var isGameOver, isGameWin = false, false;       
    var addBubbleTimer, countdownTimer = null, null;
    var lastAddTime, timeRemaining = 0, 0;       
    var countdownText = "";      
    var fallingBubbles = {};     
    
    // --- 坐标换算 ---
    var getPixelPos = function(r, c){
        var isShift = (r + gridOffsetRows) % 2 != 0;
        var x = c * DIAMETER + RADIUS + (isShift ? RADIUS : 0);
        var y = r * ROW_HEIGHT + RADIUS;
        return x, y;
    }
    
    var getDistSq = function(x1, y1, x2, y2){
        return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
    }
    
    // 泡泡绘制函数
    var drawBubble = function(graphics, clrIdx, x, y, alpha = 255){
        if(!clrIdx) return;
        var argb = (alpha << 24) | (colors[clrIdx] & 0xFFFFFF);
        var brush = gdip.solidBrush(argb);
        graphics.fillEllipse(brush, x - RADIUS, y - RADIUS, DIAMETER, DIAMETER);
        brush.delete();
        var hBrush = gdip.solidBrush((math.floor(alpha * 0.4) << 24) | 0xFFFFFF);
        graphics.fillEllipse(hBrush, x - RADIUS + 6, y - RADIUS + 6, RADIUS * 0.6, RADIUS * 0.6);
        hBrush.delete();
    }
    
    var getBubbleRowCount = function(){
        var maxR = -1;
        for(r=0; ROWS-1){
            if(!grid[r]) continue;
            for(c=0; COLS) if(grid[r][c]) { maxR = r; break; }
        }
        return maxR + 1;
    }
    
    // --- 游戏逻辑:增加行 ---
    var addNewBubbleRow = function(){
        for(r=ROWS-1; 1; -1) grid[r] = grid[r-1];
        gridOffsetRows++; 
        grid[0] = {};
        var isShift = (0 + gridOffsetRows) % 2 != 0;
        var count = isShift ? COLS - 1 : COLS;
        for(c=0; count-1) grid[0][c] = math.random(1, #colors);
        if(getBubbleRowCount() >= MAX_BUBBLE_ROWS) isGameOver = true;
        winform.canvas.redraw();
    }
    
    var getNeighbors = function(r, c){
        var isShift = (r + gridOffsetRows) % 2 != 0;
        return isShift ? {{r-1,c};{r-1,c+1};{r,c-1};{r,c+1};{r+1,c};{r+1,c+1}} 
                       : {{r-1,c-1};{r-1,c};{r,c-1};{r,c+1};{r+1,c-1};{r+1,c}};
    }
    
    var findFloating = function(){
        var visited = {};
        var queue = {};
        if(grid[0]){
            for(c=0; COLS) if(grid[0][c]) { table.push(queue, {r=0; c=c}); visited["0,"+c]=true; }
        }
        var head = 1;
        while(head <= #queue){
            var curr = queue[head]; head++;
            var os = getNeighbors(curr.r, curr.c);
            for(i=1; #os){
                var nr, nc = os[i][1], os[i][2];
                if(grid[nr] && grid[nr][nc] && !visited[nr+","+nc]){
                    visited[nr+","+nc] = true;
                    table.push(queue, {r=nr; c=nc});
                }
            }
        }
        var fl = {};
        for(r=0; ROWS-1){
            if(!grid[r]) continue;
            for(c=0; COLS) if(grid[r][c] && !visited[r+","+c]) table.push(fl, {r=r; c=c});
        }
        return fl;
    }
    
    function initGame(){
        if(addBubbleTimer) winform.clearInterval(addBubbleTimer);
        if(countdownTimer) winform.clearInterval(countdownTimer);
        grid = {}; for(r=0; ROWS) grid[r] = {};
        gridOffsetRows = 0; isGameOver = false; isGameWin = false; fallingBubbles = {};
        for(r=0; INITIAL_ROWS-1){
            var isShift = (r + gridOffsetRows) % 2 != 0;
            var count = isShift ? COLS - 1 : COLS;
            for(c=0; count-1) grid[r][c] = math.random(1, #colors);
        }
        currColor = math.random(1, #colors);
        nextColor = math.random(1, #colors);
        lastAddTime = time.tick();
        timeRemaining = GAME_DURATION;
        addBubbleTimer = winform.setInterval(ADD_ROW_INTERVAL, function(){
            if(!isGameOver) { addNewBubbleRow(); lastAddTime = time.tick(); }
        });
        countdownTimer = winform.setInterval(50, function(){
            if(isGameOver) return;
            timeRemaining -= 50;
            var remainNext = math.max(0, ADD_ROW_INTERVAL - (time.tick() - lastAddTime));
            countdownText = string.format("剩余时间: %d:%02d  |  距离塌陷: %d.%d秒", 
                timeRemaining/60000, (timeRemaining%60000)/1000, remainNext/1000, (remainNext%100)/10);
            if(#fallingBubbles > 0){
                var still = {};
                for(i=1; #fallingBubbles){
                    var f = fallingBubbles[i];
                    f.vy += 0.3; f.y += f.vy; f.alpha -= FADE_SPEED;
                    if(f.alpha > 0) table.push(still, f);
                }
                fallingBubbles = still;
            }
            if(timeRemaining <= 0) {
                isGameOver = true;
                isGameWin = (getBubbleRowCount() == 0);
            }
            winform.canvas.redraw();
        });
    }
    
    function snapBullet(){
        var minDist = 999999;
        var bestR, bestC = -1, -1;
        for(r=0; ROWS-1){
            var isShift = (r + gridOffsetRows) % 2 != 0;
            var cols = isShift ? COLS - 1 : COLS;
            for(c=0; cols-1){
                if(!grid[r][c]){
                    var px, py = getPixelPos(r, c);
                    var d = getDistSq(bullet.x, bullet.y, px, py);
                    if(d < minDist){ minDist = d; bestR, bestC = r, c; }
                }
            }
        }
        if(bestR != -1 && minDist < DIAMETER**2){
            grid[bestR][bestC] = bullet.color;
            var matches = {};
            var flood;
            flood = function(r, c, clr, list, vis){
                var k = r+","+c; if(vis[k] || !grid[r] || grid[r][c] != clr) return;
                vis[k] = true; table.push(list, {r=r; c=c});
                var os = getNeighbors(r, c);
                for(i=1;#os) flood(os[i][1], os[i][2], clr, list, vis);
            }
            flood(bestR, bestC, bullet.color, matches, {});
            if(#matches >= 3){
                for(i=1; #matches) grid[matches[i].r][matches[i].c] = null;
                var fl = findFloating();
                for(i=1; #fl){
                    var f = fl[i]; var px, py = getPixelPos(f.r, f.c);
                    table.push(fallingBubbles, {x=px; y=py; color=grid[f.r][f.c]; alpha=255; vy=2});
                    grid[f.r][f.c] = null;
                }
            }
            if(getBubbleRowCount() >= MAX_BUBBLE_ROWS) isGameOver = true;
            if(getBubbleRowCount() == 0) { isGameOver = true; isGameWin = true; }
        }
        bullet = null; currColor = nextColor; nextColor = math.random(1, #colors);
    }
    
    // --- 绘图 ---
    winform.canvas.onDrawForegroundEnd = function(graphics, rc){
    	graphics.clear(0xFF333333);
        graphics.smoothingMode = 4;
    
        // 绘制网格
        for(r=0; ROWS-1){
            if(!grid[r]) continue;
            for(c=0; COLS){
                if(grid[r][c]) {
                    var x, y = getPixelPos(r, c);
                    drawBubble(graphics, grid[r][c], x, y);
                }
            }
        }
        
        // 绘制警戒线
        var dangerY = (MAX_BUBBLE_ROWS - 1) * ROW_HEIGHT + DIAMETER;
        var penDanger = gdip.pen(0x99FF0000, 1.5);
        penDanger.dashStyle = 2;
        graphics.drawLine(penDanger, 0, dangerY, rc.width, dangerY);
        penDanger.delete();
    
    	// 绘制下落泡泡
        for(i=1; #fallingBubbles) {
            var f = fallingBubbles[i];
            drawBubble(graphics, f.color, f.x, f.y, f.alpha);
        }
        
        // 绘制发射器
        var sx, sy = rc.width/2, rc.height - 50;
        var pen = gdip.pen(0x88FFFFFF, 2);
        graphics.drawLine(pen, sx, sy, sx + math.cos(angle*math.pi/180)*60, sy + math.sin(angle*math.pi/180)*60);
        pen.delete();
        if(!bullet) drawBubble(graphics, currColor, sx, sy);
        else drawBubble(graphics, bullet.color, bullet.x, bullet.y);
    
        var nextX, nextY = rc.width - 60, rc.height - 50;
        drawBubble(graphics, nextColor, nextX, nextY, 200);
    	
    	// 绘制 NEXT
        var brushTxt = gdip.solidBrush(0xFFAAAAAA);
        gStrFormat.align = 1; // 居中
        graphics.drawString("NEXT", gFontMain, ::RECTF(nextX - 35, nextY + 25, 70, 20), gStrFormat, brushTxt);
        graphics.drawString(countdownText, gFontMain, ::RECTF(0, 15, rc.width, 30), gStrFormat, brushTxt);
        brushTxt.delete();
    
        // 游戏结束层
        if(isGameOver){
            // 半透明黑色背景层
            var maskBrush = gdip.solidBrush(0xAA000000);
            graphics.fillRectangle(maskBrush, 0, rc.height/2 - 100, rc.width, 200);
            maskBrush.delete();
    
            var title = isGameWin ? "YOU WIN!" : "GAME OVER";
            var subTitle = "";
            var color = isGameWin ? 0xFF00FF00 : 0xFFFF4444;
    
            if(isGameWin){subTitle = "完美消除!你守护了星空。";}
            else if(getBubbleRowCount() >= MAX_BUBBLE_ROWS){subTitle = "泡泡触底!防线已崩溃。";}
            else{subTitle = "时间到!恭喜你,坚持到最后:)";}
    
            var bigBrush = gdip.solidBrush(color);
            var subBrush = gdip.solidBrush(0xFFFFFFFF);
            graphics.drawString(title, gFontBig, ::RECTF(0, rc.height/2 - 70, rc.width, 60), gStrFormat, bigBrush);
            graphics.drawString(subTitle, gFontMain, ::RECTF(0, rc.height/2 + 10, rc.width, 30), gStrFormat, subBrush);
            graphics.drawString("点击屏幕重新开始", gFontMain, ::RECTF(0, rc.height/2 + 45, rc.width, 30), gStrFormat, subBrush);
    
            bigBrush.delete();
            subBrush.delete();
        }
    }
    
    // --- 事件 ---
    winform.canvas.onMouseMove = function(wParam, lParam){
        var x, y = win.getMessagePos(lParam);
        angle = math.atan2(y - (winform.canvas.height-50), x - winform.canvas.width/2) * 180 / math.pi;
        if(angle > 0) angle = angle > 90 ? -180 : 0;
    }
    
    winform.canvas.onMouseDown = function(){
        if(isGameOver) return initGame();
        if(bullet) return;
        bullet = { x=winform.canvas.width/2; y=winform.canvas.height-50; color=currColor;
                   dx=math.cos(angle*math.pi/180)*16; dy=math.sin(angle*math.pi/180)*16 };
    }
    
    winform.setInterval(16, function(){
        if(isGameOver || !bullet) return;
        bullet.x += bullet.dx; bullet.y += bullet.dy;
        if(bullet.x < RADIUS || bullet.x > winform.canvas.width-RADIUS) bullet.dx *= -1;
        if(bullet.y < RADIUS) return snapBullet();
        var hit = false;
        for(r=0; ROWS-1){
            if(!grid[r]) continue;
            for(c=0; COLS){
                if(grid[r][c]){
                    var px, py = getPixelPos(r, c);
                    if(getDistSq(bullet.x, bullet.y, px, py) < DIAMETER**2 * 0.85){ hit=true; break; }
                }
            }
            if(hit) break;
        }
        if(hit) snapBullet();
        winform.canvas.redraw();
    });
    
    initGame();
    
    winform.enableDpiScaling(false);
    winform.show();
    win.loopMessage();


返回