aardio版拼图游戏源码
By
admin
at 19 小时前 • 0人收藏 • 18人看过
Gemini改了很多次,才能正常运行, 大部分错误是对aardio一些gdip函数特性的误读导致.
后续有时间了慢慢继续改进.
先这样

import win.ui;
import win.ui.menu;
import gdip;
import gdip.region; // ★ 补全引用
import gdip.imageAttributes; // ★ 补全引用
import math;
import inet.http;
/*DSG{{*/
var winform = win.form(text="Aardio 拼图 (最终修复版)";right=1000;bottom=650;bgcolor=16777215)
winform.add(
btnLoad={cls="button";text="更换图片";left=10;top=10;right=100;bottom=40;z=2};
canvas={cls="plus";left=0;top=0;right=1000;bottom=650;db=1;dl=1;dr=1;dt=1;notify=1;z=1};
chkPreview={cls="checkbox";text="查看原图";left=110;top=15;right=200;bottom=35;z=3}
)
/*}}*/
// ==========================================
// 1. 全局配置
// ==========================================
var config = {
cols = 4;
rows = 3;
pieceSize = 120;
tabSize = 25;
sidebarWidth = 300;
snapDist = 20;
}
var game = {
imgRaw = null;
imgScaled = null;
pieces = {};
dragIndex = null;
offsetX = 0;
offsetY = 0;
boardRect = null;
showPreview = false;
}
// ==========================================
// 2. 几何算法
// ==========================================
var transformPoint = function(px, py, orientation, startX, startY, size){
if(orientation == 0){ // 上
return startX + px, startY + py;
}
elseif(orientation == 1){ // 右
return startX + size - py, startY + px;
}
elseif(orientation == 2){ // 下
return startX + size - px, startY + size - py;
}
elseif(orientation == 3){ // 左
return startX + py, startY + size - px;
}
}
var appendSidePath = function(path, x, y, sideType, orientation, size){
var tabSize = config.tabSize;
var curX, curY = transformPoint(0, 0, orientation, x, y, size);
if(sideType == 0){
var endX, endY = transformPoint(size, 0, orientation, x, y, size);
path.addLine(curX, curY, endX, endY);
return;
}
var sign = (sideType == 1) ? -1 : 1;
var h = tabSize * sign;
var w = size;
var p1x, p1y = transformPoint(w*0.35, 0, orientation, x, y, size);
path.addLine(curX, curY, p1x, p1y);
curX, curY = p1x, p1y;
var c1x, c1y = transformPoint(w*0.35, h*1.2, orientation, x, y, size);
var c2x, c2y = transformPoint(w*0.45, h, orientation, x, y, size);
var p2x, p2y = transformPoint(w*0.5, h, orientation, x, y, size);
path.addBezier(curX, curY, c1x, c1y, c2x, c2y, p2x, p2y);
curX, curY = p2x, p2y;
var c3x, c3y = transformPoint(w*0.55, h, orientation, x, y, size);
var c4x, c4y = transformPoint(w*0.65, h*1.2, orientation, x, y, size);
var p3x, p3y = transformPoint(w*0.65, 0, orientation, x, y, size);
path.addBezier(curX, curY, c3x, c3y, c4x, c4y, p3x, p3y);
curX, curY = p3x, p3y;
var endX, endY = transformPoint(w, 0, orientation, x, y, size);
path.addLine(curX, curY, endX, endY);
}
var createPiecePath = function(x, y, size, top, right, bottom, left){
var path = gdip.path();
appendSidePath(path, x, y, top, 0, size);
appendSidePath(path, x, y, right, 1, size);
appendSidePath(path, x, y, bottom, 2, size);
appendSidePath(path, x, y, left, 3, size);
path.closeFigure();
return path;
}
// ==========================================
// 3. 游戏初始化
// ==========================================
var initGame = function(imgData){
// 清理资源
if(game.imgRaw) game.imgRaw.dispose();
if(game.imgScaled) game.imgScaled.dispose();
for(k,v in game.pieces){
if(v.bitmap) v.bitmap.dispose();
if(v.path) v.path.dispose();
if(v.region) v.region.delete(); // 注意region通常用delete/dispose
}
game.pieces = {};
game.imgRaw = gdip.bitmap(imgData);
if(!game.imgRaw) return;
var viewW = winform.canvas.width - config.sidebarWidth - 40;
var viewH = winform.canvas.height - 40;
var w = game.imgRaw.width;
var h = game.imgRaw.height;
var scale = math.min(viewW/w, viewH/h);
var finalW = w * scale;
var finalH = h * scale;
config.cols = math.round(finalW / 120);
if(config.cols < 2) config.cols = 2;
config.rows = math.round(finalH / (finalW/config.cols));
if(config.rows < 2) config.rows = 2;
config.pieceSize = finalW / config.cols;
game.imgScaled = gdip.bitmap(finalW, finalH);
var g = game.imgScaled.getGraphics();
g.smoothingMode = 4;
g.drawImage(game.imgRaw, 0, 0, finalW, finalH);
g.dispose();
var boardX = config.sidebarWidth + (winform.canvas.width - config.sidebarWidth - finalW)/2;
var boardY = (winform.canvas.height - finalH)/2;
game.boardRect = { x=boardX; y=boardY; w=finalW; h=finalH };
var vEdges = {};
var hEdges = {};
for(r=1; config.rows){
vEdges[r] = {};
for(c=1; config.cols-1) vEdges[r][c] = (math.random(0,1)==0) ? 1 : -1;
}
for(r=1; config.rows-1){
hEdges[r] = {};
for(c=1; config.cols) hEdges[r][c] = (math.random(0,1)==0) ? 1 : -1;
}
for(r=1; config.rows){
for(c=1; config.cols){
var p = {};
p.row = r; p.col = c;
p.top = (r > 1) ? -hEdges[r-1][c] : 0;
p.bottom = (r < config.rows) ? hEdges[r][c] : 0;
p.left = (c > 1) ? -vEdges[r][c-1] : 0;
p.right = (c < config.cols) ? vEdges[r][c] : 0;
p.targetX = (c-1) * config.pieceSize;
p.targetY = (r-1) * config.pieceSize;
p.x = math.random(20, config.sidebarWidth - config.pieceSize - 20);
p.y = math.random(60, winform.canvas.height - config.pieceSize - 20);
p.isLocked = false;
p.path = createPiecePath(0, 0, config.pieceSize, p.top, p.right, p.bottom, p.left);
// 依然创建region以备不时之需,但点击检测改用数学计算
p.region = gdip.region(p.path);
var pad = config.tabSize;
var bmpW = config.pieceSize + pad*2;
var bmpH = config.pieceSize + pad*2;
var pieceBmp = gdip.bitmap(bmpW, bmpH);
var pg = pieceBmp.getGraphics();
pg.smoothingMode = 4;
// ★ 改正:使用 translate 代替 translateTransform
pg.translate(pad, pad);
pg.setClipPath(p.path);
pg.drawImage(game.imgScaled, -p.targetX, -p.targetY, game.boardRect.w, game.boardRect.h);
var pen = gdip.pen(0x80000000, 1);
pg.resetClip();
pg.drawPath(pen, p.path);
pen.dispose();
pg.dispose();
p.bitmap = pieceBmp;
table.push(game.pieces, p);
}
}
winform.canvas.redraw();
}
// ==========================================
// 4. 绘图渲染
// ==========================================
winform.canvas.onDrawForegroundEnd = function(graphics, rc){
if(!game.imgScaled) return;
graphics.smoothingMode = 4;
var brushSide = gdip.solidBrush(0xFFE0E0E0);
graphics.fillRectangle(brushSide, 0, 0, config.sidebarWidth, rc.bottom);
brushSide.dispose();
var brushBoard = gdip.solidBrush(0xFFF0F0F0);
graphics.fillRectangle(brushBoard, config.sidebarWidth, 0, rc.right-config.sidebarWidth, rc.bottom);
brushBoard.dispose();
var penSplit = gdip.pen(0xFF999999, 2);
graphics.drawLine(penSplit, config.sidebarWidth, 0, config.sidebarWidth, rc.bottom);
penSplit.dispose();
var attr = gdip.imageAttributes();
var colorMatrix = { 1;0;0;0;0; 0;1;0;0;0; 0;0;1;0;0; 0;0;0;0.3;0; 0;0;0;0;1 };
attr.setColorMatrix(colorMatrix);
graphics.drawImage(game.imgScaled, game.boardRect.x, game.boardRect.y, game.boardRect.w, game.boardRect.h, attr);
attr.dispose();
var drawPiece = function(p, isDragging){
if(!p.bitmap) return;
var drawX = p.x;
var drawY = p.y;
if(p.isLocked){
drawX = game.boardRect.x + p.targetX;
drawY = game.boardRect.y + p.targetY;
}
var pad = config.tabSize;
if(isDragging){
var brushShadow = gdip.solidBrush(0x50000000);
// ★ 改正:使用 translate
graphics.translate(drawX - pad + 5, drawY - pad + 5);
graphics.fillPath(brushShadow, p.path);
graphics.resetTransform();
brushShadow.dispose();
}
graphics.drawImage(p.bitmap, drawX - pad, drawY - pad);
}
for(i=1; #game.pieces){
if(game.pieces[i].isLocked) drawPiece(game.pieces[i], false);
}
for(i=1; #game.pieces){
var p = game.pieces[i];
if(!p.isLocked && i != game.dragIndex) drawPiece(p, false);
}
if(game.dragIndex && game.pieces[game.dragIndex]){
drawPiece(game.pieces[game.dragIndex], true);
}
if(game.showPreview){
var preW = 200;
var preH = preW * (game.imgRaw.height / game.imgRaw.width);
var preX = (rc.right - preW) / 2 + config.sidebarWidth/2;
var preY = (rc.bottom - preH) / 2;
var brushBox = gdip.solidBrush(0xFFFFFFFF);
graphics.fillRectangle(brushBox, preX-5, preY-5, preW+10, preH+10);
graphics.drawRectangle(gdip.pen(0xFF000000,1), preX-5, preY-5, preW+10, preH+10);
graphics.drawImage(game.imgRaw, preX, preY, preW, preH);
brushBox.dispose();
}
}
// ==========================================
// 5. 交互逻辑 (修改版)
// ==========================================
winform.canvas.onMouseDown = function(wParam,lParam){
var x,y = win.getMessagePos(lParam);
for(i=#game.pieces; 1; -1){
var p = game.pieces[i];
if(p.isLocked) continue;
// ★★★ 核心修复:完全放弃依赖 isVisible 函数,改用数学矩形判定 ★★★
// 这样绝对不会报错 "no function"。
// 判定范围:拼图主体(config.pieceSize) + 四周的突起范围(config.tabSize)
// 这种判定对于拼图游戏来说足够精确且性能极高。
var pad = config.tabSize;
// 鼠标点击区域检查
if( x >= p.x - pad && x <= p.x + config.pieceSize + pad &&
y >= p.y - pad && y <= p.y + config.pieceSize + pad ){
game.dragIndex = i;
game.offsetX = x - p.x;
game.offsetY = y - p.y;
table.remove(game.pieces, i);
table.push(game.pieces, p);
game.dragIndex = #game.pieces;
winform.canvas.redraw();
return;
}
}
}
winform.canvas.onMouseMove = function(wParam,lParam){
if(game.dragIndex){
var x,y = win.getMessagePos(lParam);
var p = game.pieces[game.dragIndex];
if(p){
p.x = x - game.offsetX;
p.y = y - game.offsetY;
winform.canvas.redraw();
}
}
}
winform.canvas.onMouseUp = function(wParam,lParam){
if(game.dragIndex){
var p = game.pieces[game.dragIndex];
if(p){
var absTargetX = game.boardRect.x + p.targetX;
var absTargetY = game.boardRect.y + p.targetY;
if( math.abs(p.x - absTargetX) < config.snapDist &&
math.abs(p.y - absTargetY) < config.snapDist ){
p.x = absTargetX;
p.y = absTargetY;
p.isLocked = true;
}
}
game.dragIndex = null;
winform.canvas.redraw();
var winState = true;
for(i=1; #game.pieces){
if(!game.pieces[i].isLocked){ winState = false; break; }
}
if(winState) winform.msgbox("恭喜!拼图完成!");
}
}
winform.btnLoad.oncommand = function(id,event){
var path = win.ui.ctrl.common.file.open("图片文件|*.jpg;*.png;*.bmp");
if(path){
var buf = string.load(path);
if(buf) initGame(buf);
}
}
winform.chkPreview.oncommand = function(id,event){
game.showPreview = winform.chkPreview.checked;
winform.canvas.redraw();
}
winform.setTimeout(function(){
var url = "https://images.unsplash.com/photo-1587614382346-4ec70e388b28?w=600&q=80";
var buf = inet.http().get(url);
if(buf) initGame(buf);
}, 100);
winform.show();
win.loopMessage();登录后方可回帖