feat: 初始版本
@@ -0,0 +1,706 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
namespace DesktopRoachRampage
|
||||||
|
{
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CockroachState
|
||||||
|
{
|
||||||
|
Walking,
|
||||||
|
Flying,
|
||||||
|
Dead // 死亡状态
|
||||||
|
}
|
||||||
|
|
||||||
|
class Cockroach
|
||||||
|
{
|
||||||
|
public float X { get; set; }
|
||||||
|
public float Y { get; set; }
|
||||||
|
public float DirectionX { get; set; }
|
||||||
|
public float DirectionY { get; set; }
|
||||||
|
public float CurrentAngle { get; set; }
|
||||||
|
public int CurrentFrame { get; set; }
|
||||||
|
public Random Random { get; set; }
|
||||||
|
public CockroachState State { get; set; }
|
||||||
|
public float FlyHeightOffset { get; set; }
|
||||||
|
public float StateTimer { get; set; }
|
||||||
|
public float DeathTimer { get; set; } // 死亡后消失计时器
|
||||||
|
public float Scale { get; set; } // 缩放比例,死亡时缩小
|
||||||
|
|
||||||
|
public Cockroach(float x, float y, Random rand)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Random = rand;
|
||||||
|
State = CockroachState.Walking;
|
||||||
|
FlyHeightOffset = 0;
|
||||||
|
StateTimer = 0;
|
||||||
|
DeathTimer = 0;
|
||||||
|
Scale = 1f;
|
||||||
|
|
||||||
|
double angle = rand.NextDouble() * Math.PI * 2;
|
||||||
|
float speed = 2f + (float)rand.NextDouble() * 2f;
|
||||||
|
DirectionX = (float)Math.Cos(angle) * speed;
|
||||||
|
DirectionY = (float)Math.Sin(angle) * speed;
|
||||||
|
|
||||||
|
CurrentAngle = (float)(angle * 180 / Math.PI) + 90f;
|
||||||
|
CurrentFrame = rand.Next(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 被打死
|
||||||
|
public void Kill()
|
||||||
|
{
|
||||||
|
if (State == CockroachState.Dead) return;
|
||||||
|
|
||||||
|
State = CockroachState.Dead;
|
||||||
|
DeathTimer = 0;
|
||||||
|
DirectionX = 0;
|
||||||
|
DirectionY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(Rectangle totalBounds, int petSize)
|
||||||
|
{
|
||||||
|
// 如果已经死亡,处理消失动画
|
||||||
|
if (State == CockroachState.Dead)
|
||||||
|
{
|
||||||
|
DeathTimer++;
|
||||||
|
Scale = Math.Max(0, 1f - DeathTimer / 30f); // 30帧内缩小到消失
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StateTimer++;
|
||||||
|
|
||||||
|
// 更频繁检查状态切换(约5秒),飞行概率更高
|
||||||
|
if (StateTimer > 100)
|
||||||
|
{
|
||||||
|
StateTimer = 0;
|
||||||
|
if (Random.NextDouble() < 0.7) // 70%概率切换
|
||||||
|
{
|
||||||
|
if (State == CockroachState.Walking)
|
||||||
|
{
|
||||||
|
// 开始飞行 - 速度大幅提升
|
||||||
|
State = CockroachState.Flying;
|
||||||
|
float flySpeed = 7f + (float)Random.NextDouble() * 4f; // 飞行速度7-11
|
||||||
|
double flyAngle = Random.NextDouble() * Math.PI * 2;
|
||||||
|
DirectionX = (float)Math.Cos(flyAngle) * flySpeed;
|
||||||
|
DirectionY = (float)Math.Sin(flyAngle) * flySpeed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 降落
|
||||||
|
State = CockroachState.Walking;
|
||||||
|
FlyHeightOffset = 0;
|
||||||
|
float walkSpeed = 2f + (float)Random.NextDouble() * 2f;
|
||||||
|
double walkAngle = Random.NextDouble() * Math.PI * 2;
|
||||||
|
DirectionX = (float)Math.Cos(walkAngle) * walkSpeed;
|
||||||
|
DirectionY = (float)Math.Sin(walkAngle) * walkSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 飞行时高度偏移
|
||||||
|
if (State == CockroachState.Flying)
|
||||||
|
{
|
||||||
|
FlyHeightOffset = (float)Math.Sin(StateTimer * 0.1) * 5f - 8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动
|
||||||
|
X += DirectionX;
|
||||||
|
Y += DirectionY;
|
||||||
|
|
||||||
|
// 边界检测与反弹
|
||||||
|
if (X <= totalBounds.Left || X >= totalBounds.Right - petSize)
|
||||||
|
{
|
||||||
|
DirectionX = -DirectionX + (float)(Random.NextDouble() - 0.5) * 2;
|
||||||
|
float minSpeed = State == CockroachState.Flying ? 6f : 1.5f;
|
||||||
|
if (Math.Abs(DirectionX) < minSpeed) DirectionX = DirectionX > 0 ? minSpeed + 1 : -minSpeed - 1;
|
||||||
|
X = Math.Max(totalBounds.Left, Math.Min(X, totalBounds.Right - petSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Y <= totalBounds.Top || Y >= totalBounds.Bottom - petSize)
|
||||||
|
{
|
||||||
|
DirectionY = -DirectionY + (float)(Random.NextDouble() - 0.5) * 2;
|
||||||
|
float minSpeed = State == CockroachState.Flying ? 6f : 1.5f;
|
||||||
|
if (Math.Abs(DirectionY) < minSpeed) DirectionY = DirectionY > 0 ? minSpeed + 1 : -minSpeed - 1;
|
||||||
|
Y = Math.Max(totalBounds.Top, Math.Min(Y, totalBounds.Bottom - petSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机改变方向
|
||||||
|
if (Random.Next(100) < 5)
|
||||||
|
{
|
||||||
|
float changeRange = State == CockroachState.Flying ? 6f : 3f;
|
||||||
|
DirectionX += (float)(Random.NextDouble() - 0.5) * changeRange;
|
||||||
|
DirectionY += (float)(Random.NextDouble() - 0.5) * changeRange;
|
||||||
|
float maxSpeed = State == CockroachState.Flying ? 12f : 5f;
|
||||||
|
DirectionX = Math.Max(-maxSpeed, Math.Min(maxSpeed, DirectionX));
|
||||||
|
DirectionY = Math.Max(-maxSpeed, Math.Min(maxSpeed, DirectionY));
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentFrame = (CurrentFrame + 1) % 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(Graphics g, Image[] walkFrames, Image[] flyFrames, int petSize)
|
||||||
|
{
|
||||||
|
Image[] frames = State == CockroachState.Flying ? flyFrames : walkFrames;
|
||||||
|
if (frames == null || frames.Length == 0) return;
|
||||||
|
|
||||||
|
Image frame = frames[CurrentFrame % frames.Length];
|
||||||
|
if (frame == null) return;
|
||||||
|
|
||||||
|
float targetAngle = (float)(Math.Atan2(DirectionY, DirectionX) * 180 / Math.PI) + 90f;
|
||||||
|
|
||||||
|
float angleDiff = targetAngle - CurrentAngle;
|
||||||
|
if (angleDiff > 180) angleDiff -= 360;
|
||||||
|
if (angleDiff < -180) angleDiff += 360;
|
||||||
|
CurrentAngle += angleDiff * 0.15f;
|
||||||
|
|
||||||
|
Matrix oldMatrix = g.Transform;
|
||||||
|
|
||||||
|
float drawY = Y + FlyHeightOffset;
|
||||||
|
float drawSize = petSize * Scale;
|
||||||
|
|
||||||
|
// 绘制缩放和旋转
|
||||||
|
g.TranslateTransform(X + drawSize / 2, drawY + drawSize / 2);
|
||||||
|
g.RotateTransform(CurrentAngle);
|
||||||
|
g.ScaleTransform(Scale, Scale);
|
||||||
|
g.TranslateTransform(-drawSize / 2, -drawSize / 2);
|
||||||
|
|
||||||
|
g.DrawImage(frame, 0, 0, drawSize, drawSize);
|
||||||
|
|
||||||
|
g.Transform = oldMatrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击特效类
|
||||||
|
class ClickEffect
|
||||||
|
{
|
||||||
|
public float X { get; set; }
|
||||||
|
public float Y { get; set; }
|
||||||
|
public float Timer { get; set; }
|
||||||
|
public const float MaxTime = 15f; // 持续15帧
|
||||||
|
|
||||||
|
public ClickEffect(float x, float y)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Timer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
Timer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(Graphics g)
|
||||||
|
{
|
||||||
|
float progress = Timer / MaxTime;
|
||||||
|
float size = 20 + progress * 30; // 从20扩散到50
|
||||||
|
float alpha = 1f - progress; // 逐渐透明
|
||||||
|
|
||||||
|
using (Pen pen = new Pen(Color.FromArgb((int)(alpha * 200), 255, 100, 100), 3))
|
||||||
|
{
|
||||||
|
g.DrawEllipse(pen, X - size / 2, Y - size / 2, size, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDead()
|
||||||
|
{
|
||||||
|
return Timer >= MaxTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainForm : Form
|
||||||
|
{
|
||||||
|
private List<Cockroach> cockroaches;
|
||||||
|
private Image[] walkFrames;
|
||||||
|
private Image[] flyFrames;
|
||||||
|
private Timer mainTimer;
|
||||||
|
private Timer breedTimer;
|
||||||
|
private Random random;
|
||||||
|
private const int PetSize = 64;
|
||||||
|
private bool dragging = false;
|
||||||
|
private int dragIndex = -1;
|
||||||
|
private Point dragOffset;
|
||||||
|
private const int MaxCockroaches = 50;
|
||||||
|
private Rectangle totalScreenBounds;
|
||||||
|
private Bitmap bufferBitmap;
|
||||||
|
private Graphics bufferGraphics;
|
||||||
|
|
||||||
|
// 打蟑螂相关
|
||||||
|
private int score = 0; // 分数
|
||||||
|
private int lastScore = 0; // 上次的分数
|
||||||
|
private float scoreIdleTimer = 0; // 分数没变化的时间
|
||||||
|
private bool showScore = true; // 是否显示分数
|
||||||
|
private List<ClickEffect> clickEffects; // 点击特效
|
||||||
|
private Font scoreFont;
|
||||||
|
|
||||||
|
public MainForm()
|
||||||
|
{
|
||||||
|
random = new Random();
|
||||||
|
cockroaches = new List<Cockroach>();
|
||||||
|
clickEffects = new List<ClickEffect>();
|
||||||
|
|
||||||
|
// 启用双缓冲优化
|
||||||
|
this.SetStyle(ControlStyles.DoubleBuffer |
|
||||||
|
ControlStyles.UserPaint |
|
||||||
|
ControlStyles.AllPaintingInWmPaint |
|
||||||
|
ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
this.UpdateStyles();
|
||||||
|
|
||||||
|
CalculateTotalScreenBounds();
|
||||||
|
|
||||||
|
this.FormBorderStyle = FormBorderStyle.None;
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
|
this.TopMost = true;
|
||||||
|
this.TransparencyKey = Color.Magenta;
|
||||||
|
this.BackColor = Color.Magenta;
|
||||||
|
this.Bounds = totalScreenBounds;
|
||||||
|
|
||||||
|
LoadFrames();
|
||||||
|
|
||||||
|
// 创建双缓冲位图
|
||||||
|
CreateBufferBitmap();
|
||||||
|
|
||||||
|
// 创建分数字体
|
||||||
|
scoreFont = new Font("Arial", 14f, FontStyle.Bold);
|
||||||
|
|
||||||
|
SpawnCockroachOnPrimary();
|
||||||
|
|
||||||
|
mainTimer = new Timer();
|
||||||
|
mainTimer.Interval = 50;
|
||||||
|
mainTimer.Tick += MainTimer_Tick;
|
||||||
|
mainTimer.Start();
|
||||||
|
|
||||||
|
breedTimer = new Timer();
|
||||||
|
breedTimer.Interval = 8000;
|
||||||
|
breedTimer.Tick += BreedTimer_Tick;
|
||||||
|
breedTimer.Start();
|
||||||
|
|
||||||
|
ContextMenuStrip menu = new ContextMenuStrip();
|
||||||
|
menu.Items.Add("清除所有蟑螂", null, (s, e) => ClearAll());
|
||||||
|
menu.Items.Add("添加一只蟑螂", null, (s, e) => SpawnCockroach());
|
||||||
|
menu.Items.Add("让所有蟑螂起飞!", null, (s, e) => MakeAllFly());
|
||||||
|
menu.Items.Add("让所有蟑螂降落!", null, (s, e) => MakeAllLand());
|
||||||
|
menu.Items.Add("随机分布到所有屏幕", null, (s, e) => RedistributeAll());
|
||||||
|
menu.Items.Add("-", null, null);
|
||||||
|
menu.Items.Add("重置分数", null, (s, e) => ResetScore());
|
||||||
|
menu.Items.Add("-", null, null);
|
||||||
|
menu.Items.Add("Exit / 退出", null, (s, e) => Application.Exit());
|
||||||
|
this.ContextMenuStrip = menu;
|
||||||
|
|
||||||
|
this.MouseDown += MainForm_MouseDown;
|
||||||
|
this.MouseMove += MainForm_MouseMove;
|
||||||
|
this.MouseUp += MainForm_MouseUp;
|
||||||
|
|
||||||
|
SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetScore()
|
||||||
|
{
|
||||||
|
score = 0;
|
||||||
|
lastScore = 0;
|
||||||
|
showScore = true;
|
||||||
|
scoreIdleTimer = 0;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateBufferBitmap()
|
||||||
|
{
|
||||||
|
if (bufferBitmap != null)
|
||||||
|
{
|
||||||
|
bufferBitmap.Dispose();
|
||||||
|
bufferGraphics.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferBitmap = new Bitmap(totalScreenBounds.Width, totalScreenBounds.Height, PixelFormat.Format32bppArgb);
|
||||||
|
bufferGraphics = Graphics.FromImage(bufferBitmap);
|
||||||
|
bufferGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||||
|
bufferGraphics.SmoothingMode = SmoothingMode.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CalculateTotalScreenBounds()
|
||||||
|
{
|
||||||
|
totalScreenBounds = Rectangle.Empty;
|
||||||
|
foreach (Screen screen in Screen.AllScreens)
|
||||||
|
{
|
||||||
|
totalScreenBounds = Rectangle.Union(totalScreenBounds, screen.Bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
CalculateTotalScreenBounds();
|
||||||
|
this.Bounds = totalScreenBounds;
|
||||||
|
CreateBufferBitmap();
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnCockroachOnPrimary()
|
||||||
|
{
|
||||||
|
if (cockroaches.Count >= MaxCockroaches) return;
|
||||||
|
|
||||||
|
Rectangle primary = Screen.PrimaryScreen.WorkingArea;
|
||||||
|
float x = primary.Left + random.Next(100, primary.Width - 100 - PetSize);
|
||||||
|
float y = primary.Top + random.Next(100, primary.Height - 100 - PetSize);
|
||||||
|
|
||||||
|
cockroaches.Add(new Cockroach(x, y, random));
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnCockroach()
|
||||||
|
{
|
||||||
|
if (cockroaches.Count >= MaxCockroaches) return;
|
||||||
|
|
||||||
|
Screen[] screens = Screen.AllScreens;
|
||||||
|
Screen randomScreen = screens[random.Next(screens.Length)];
|
||||||
|
Rectangle workingArea = randomScreen.WorkingArea;
|
||||||
|
|
||||||
|
float x = workingArea.Left + random.Next(50, workingArea.Width - 50 - PetSize);
|
||||||
|
float y = workingArea.Top + random.Next(50, workingArea.Height - 50 - PetSize);
|
||||||
|
|
||||||
|
cockroaches.Add(new Cockroach(x, y, random));
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MakeAllFly()
|
||||||
|
{
|
||||||
|
foreach (var roach in cockroaches)
|
||||||
|
{
|
||||||
|
if (roach.State != CockroachState.Dead)
|
||||||
|
{
|
||||||
|
roach.State = CockroachState.Flying;
|
||||||
|
roach.StateTimer = 0;
|
||||||
|
float flySpeed = 7f + (float)random.NextDouble() * 4f;
|
||||||
|
double flyAngle = random.NextDouble() * Math.PI * 2;
|
||||||
|
roach.DirectionX = (float)Math.Cos(flyAngle) * flySpeed;
|
||||||
|
roach.DirectionY = (float)Math.Sin(flyAngle) * flySpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MakeAllLand()
|
||||||
|
{
|
||||||
|
foreach (var roach in cockroaches)
|
||||||
|
{
|
||||||
|
if (roach.State != CockroachState.Dead)
|
||||||
|
{
|
||||||
|
roach.State = CockroachState.Walking;
|
||||||
|
roach.FlyHeightOffset = 0;
|
||||||
|
roach.StateTimer = 0;
|
||||||
|
float walkSpeed = 2f + (float)random.NextDouble() * 2f;
|
||||||
|
double walkAngle = random.NextDouble() * Math.PI * 2;
|
||||||
|
roach.DirectionX = (float)Math.Cos(walkAngle) * walkSpeed;
|
||||||
|
roach.DirectionY = (float)Math.Sin(walkAngle) * walkSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RedistributeAll()
|
||||||
|
{
|
||||||
|
Screen[] screens = Screen.AllScreens;
|
||||||
|
foreach (var roach in cockroaches)
|
||||||
|
{
|
||||||
|
if (roach.State != CockroachState.Dead)
|
||||||
|
{
|
||||||
|
Screen randomScreen = screens[random.Next(screens.Length)];
|
||||||
|
Rectangle workingArea = randomScreen.WorkingArea;
|
||||||
|
|
||||||
|
roach.X = workingArea.Left + random.Next(50, workingArea.Width - 50 - PetSize);
|
||||||
|
roach.Y = workingArea.Top + random.Next(50, workingArea.Height - 50 - PetSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTitle()
|
||||||
|
{
|
||||||
|
int flyCount = 0;
|
||||||
|
int walkCount = 0;
|
||||||
|
int deadCount = 0;
|
||||||
|
foreach (var roach in cockroaches)
|
||||||
|
{
|
||||||
|
if (roach.State == CockroachState.Flying)
|
||||||
|
flyCount++;
|
||||||
|
else if (roach.State == CockroachState.Walking)
|
||||||
|
walkCount++;
|
||||||
|
else
|
||||||
|
deadCount++;
|
||||||
|
}
|
||||||
|
this.Text = string.Format("Desktop Roach Rampage - {0} roaches alive (flying:{1} walking:{2}) - killed: {3}",
|
||||||
|
cockroaches.Count - deadCount, flyCount, walkCount, score);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BreedTimer_Tick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// 检查是否所有蟑螂都被消灭了
|
||||||
|
int aliveCount = cockroaches.FindAll(r => r.State != CockroachState.Dead).Count;
|
||||||
|
|
||||||
|
if (aliveCount == 0)
|
||||||
|
{
|
||||||
|
// 蟑螂全被消灭了,自动生成一只
|
||||||
|
SpawnCockroachOnPrimary();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentCount = cockroaches.Count;
|
||||||
|
for (int i = 0; i < currentCount && cockroaches.Count < MaxCockroaches; i++)
|
||||||
|
{
|
||||||
|
// 飞行中的蟑螂繁殖概率更高
|
||||||
|
double breedChance = cockroaches[i].State == CockroachState.Flying ? 0.6 : 0.4;
|
||||||
|
if (cockroaches[i].State != CockroachState.Dead && random.NextDouble() < breedChance)
|
||||||
|
{
|
||||||
|
Cockroach parent = cockroaches[i];
|
||||||
|
float offsetX = (float)(random.NextDouble() - 0.5) * 80;
|
||||||
|
float offsetY = (float)(random.NextDouble() - 0.5) * 80;
|
||||||
|
|
||||||
|
float newX = parent.X + offsetX;
|
||||||
|
float newY = parent.Y + offsetY;
|
||||||
|
|
||||||
|
newX = Math.Max(totalScreenBounds.Left, Math.Min(newX, totalScreenBounds.Right - PetSize));
|
||||||
|
newY = Math.Max(totalScreenBounds.Top, Math.Min(newY, totalScreenBounds.Bottom - PetSize));
|
||||||
|
|
||||||
|
cockroaches.Add(new Cockroach(newX, newY, random));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearAll()
|
||||||
|
{
|
||||||
|
cockroaches.Clear();
|
||||||
|
SpawnCockroachOnPrimary();
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainForm_MouseDown(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Button == MouseButtons.Left)
|
||||||
|
{
|
||||||
|
Point mousePos = this.PointToClient(e.Location);
|
||||||
|
|
||||||
|
// 从后往前检测(点到最上面的蟑螂)
|
||||||
|
bool hit = false;
|
||||||
|
for (int i = cockroaches.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
Cockroach roach = cockroaches[i];
|
||||||
|
if (roach.State == CockroachState.Dead) continue;
|
||||||
|
|
||||||
|
float drawY = roach.Y + roach.FlyHeightOffset;
|
||||||
|
RectangleF rect = new RectangleF(roach.X - this.Left, drawY - this.Top, PetSize, PetSize);
|
||||||
|
if (rect.Contains(mousePos))
|
||||||
|
{
|
||||||
|
// 打到蟑螂了!
|
||||||
|
roach.Kill();
|
||||||
|
score++;
|
||||||
|
hit = true;
|
||||||
|
showScore = true; // 立即显示分数
|
||||||
|
scoreIdleTimer = 0;
|
||||||
|
|
||||||
|
// 添加点击特效
|
||||||
|
float effectX = mousePos.X + this.Left;
|
||||||
|
float effectY = mousePos.Y + this.Top;
|
||||||
|
clickEffects.Add(new ClickEffect(effectX, effectY));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没打到,也添加一个空的点击特效
|
||||||
|
if (!hit)
|
||||||
|
{
|
||||||
|
float effectX = mousePos.X + this.Left;
|
||||||
|
float effectY = mousePos.Y + this.Top;
|
||||||
|
clickEffects.Add(new ClickEffect(effectX, effectY));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否拖动(只有没打到蟑螂时才拖动)
|
||||||
|
if (!hit)
|
||||||
|
{
|
||||||
|
for (int i = cockroaches.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
Cockroach roach = cockroaches[i];
|
||||||
|
if (roach.State == CockroachState.Dead) continue;
|
||||||
|
|
||||||
|
float drawY = roach.Y + roach.FlyHeightOffset;
|
||||||
|
RectangleF rect = new RectangleF(roach.X - this.Left, drawY - this.Top, PetSize, PetSize);
|
||||||
|
if (rect.Contains(mousePos))
|
||||||
|
{
|
||||||
|
dragging = true;
|
||||||
|
dragIndex = i;
|
||||||
|
dragOffset = new Point(mousePos.X - (int)(roach.X - this.Left), mousePos.Y - (int)(drawY - this.Top));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainForm_MouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (dragging && dragIndex >= 0 && dragIndex < cockroaches.Count)
|
||||||
|
{
|
||||||
|
Point mousePos = this.PointToClient(e.Location);
|
||||||
|
Cockroach roach = cockroaches[dragIndex];
|
||||||
|
roach.X = mousePos.X - dragOffset.X + this.Left;
|
||||||
|
roach.Y = mousePos.Y - dragOffset.Y + this.Top - roach.FlyHeightOffset;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainForm_MouseUp(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
dragging = false;
|
||||||
|
dragIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFrames()
|
||||||
|
{
|
||||||
|
string basePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
string imagePath = Path.Combine(basePath, "images");
|
||||||
|
|
||||||
|
if (!Directory.Exists(imagePath))
|
||||||
|
{
|
||||||
|
imagePath = Path.Combine(basePath, "..", "images");
|
||||||
|
}
|
||||||
|
|
||||||
|
walkFrames = new Image[8];
|
||||||
|
flyFrames = new Image[8];
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
string walkFrameFile = Path.Combine(imagePath, string.Format("walk_{0:D2}.png", i));
|
||||||
|
if (File.Exists(walkFrameFile))
|
||||||
|
walkFrames[i] = Image.FromFile(walkFrameFile);
|
||||||
|
|
||||||
|
string flyFrameFile = Path.Combine(imagePath, string.Format("fly_{0:D2}.png", i));
|
||||||
|
if (File.Exists(flyFrameFile))
|
||||||
|
flyFrames[i] = Image.FromFile(flyFrameFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainTimer_Tick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// 更新蟑螂
|
||||||
|
foreach (var roach in cockroaches)
|
||||||
|
{
|
||||||
|
roach.Update(totalScreenBounds, PetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除完全消失的死蟑螂
|
||||||
|
cockroaches.RemoveAll(r => r.State == CockroachState.Dead && r.Scale <= 0);
|
||||||
|
|
||||||
|
// 更新点击特效
|
||||||
|
for (int i = clickEffects.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
clickEffects[i].Update();
|
||||||
|
if (clickEffects[i].IsDead())
|
||||||
|
{
|
||||||
|
clickEffects.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分数显示逻辑:分数变化时显示,2秒不变化则隐藏
|
||||||
|
if (score != lastScore)
|
||||||
|
{
|
||||||
|
lastScore = score;
|
||||||
|
scoreIdleTimer = 0;
|
||||||
|
showScore = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scoreIdleTimer++;
|
||||||
|
// 50ms每帧,40帧 = 2秒
|
||||||
|
if (scoreIdleTimer >= 40)
|
||||||
|
{
|
||||||
|
showScore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e)
|
||||||
|
{
|
||||||
|
// 使用双缓冲绘制
|
||||||
|
Graphics g = bufferGraphics;
|
||||||
|
|
||||||
|
g.Clear(Color.Magenta);
|
||||||
|
|
||||||
|
g.TranslateTransform(-this.Left, -this.Top);
|
||||||
|
|
||||||
|
// 绘制蟑螂
|
||||||
|
foreach (var roach in cockroaches)
|
||||||
|
{
|
||||||
|
roach.Draw(g, walkFrames, flyFrames, PetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制点击特效
|
||||||
|
foreach (var effect in clickEffects)
|
||||||
|
{
|
||||||
|
effect.Draw(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制分数(在主屏幕左上角)
|
||||||
|
if (showScore)
|
||||||
|
{
|
||||||
|
g.ResetTransform();
|
||||||
|
g.TranslateTransform(Screen.PrimaryScreen.Bounds.Left - this.Left,
|
||||||
|
Screen.PrimaryScreen.Bounds.Top - this.Top);
|
||||||
|
|
||||||
|
string scoreText = string.Format("Killed: {0}", score);
|
||||||
|
g.DrawString(scoreText, scoreFont, Brushes.Red, 10, 10);
|
||||||
|
|
||||||
|
// 绘制剩余数量
|
||||||
|
int alive = cockroaches.FindAll(r => r.State != CockroachState.Dead).Count;
|
||||||
|
string countText = string.Format("Alive: {0}", alive);
|
||||||
|
g.DrawString(countText, scoreFont, Brushes.DarkOrange, 10, 35);
|
||||||
|
|
||||||
|
g.ResetTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一次性绘制到屏幕
|
||||||
|
e.Graphics.DrawImage(bufferBitmap, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged;
|
||||||
|
if (mainTimer != null) mainTimer.Dispose();
|
||||||
|
if (breedTimer != null) breedTimer.Dispose();
|
||||||
|
if (bufferBitmap != null) bufferBitmap.Dispose();
|
||||||
|
if (bufferGraphics != null) bufferGraphics.Dispose();
|
||||||
|
if (scoreFont != null) scoreFont.Dispose();
|
||||||
|
if (walkFrames != null)
|
||||||
|
foreach (var frame in walkFrames)
|
||||||
|
if (frame != null) frame.Dispose();
|
||||||
|
if (flyFrames != null)
|
||||||
|
foreach (var frame in flyFrames)
|
||||||
|
if (frame != null) frame.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 633 B |
|
After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 642 B |
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 652 B |
|
After Width: | Height: | Size: 634 B |
|
After Width: | Height: | Size: 629 B |
|
After Width: | Height: | Size: 649 B |
|
After Width: | Height: | Size: 570 B |
|
After Width: | Height: | Size: 578 B |
|
After Width: | Height: | Size: 582 B |
|
After Width: | Height: | Size: 590 B |
|
After Width: | Height: | Size: 585 B |
|
After Width: | Height: | Size: 595 B |
|
After Width: | Height: | Size: 602 B |
|
After Width: | Height: | Size: 589 B |
@@ -0,0 +1,24 @@
|
|||||||
|
@echo off
|
||||||
|
REM Build script for Desktop Roach Rampage
|
||||||
|
REM Compiles the C# source code into a Windows executable
|
||||||
|
|
||||||
|
echo Building Desktop Roach Rampage...
|
||||||
|
cd /d "%~dp0app"
|
||||||
|
|
||||||
|
REM Use .NET Framework compiler
|
||||||
|
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:winexe /r:System.dll /r:System.Drawing.dll /r:System.Windows.Forms.dll /out:DesktopRoachRampage.exe Program.cs
|
||||||
|
|
||||||
|
if %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo Build failed!
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Copying executable to bin directory...
|
||||||
|
if not exist "..\bin" mkdir "..\bin"
|
||||||
|
copy /Y "DesktopRoachRampage.exe" "..\bin\" > nul
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Build completed successfully!
|
||||||
|
echo Executable: DesktopRoachRampage\bin\DesktopRoachRampage.exe
|
||||||
|
pause
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
REM Run Desktop Roach Rampage
|
||||||
|
|
||||||
|
cd /d "%~dp0bin"
|
||||||
|
if not exist "DesktopRoachRampage.exe" (
|
||||||
|
echo Executable not found! Please run build.bat first.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Starting Desktop Roach Rampage...
|
||||||
|
start "" DesktopRoachRampage.exe
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
class GenerateCockroaches
|
||||||
|
{
|
||||||
|
static string saveDir = @"e:\Temp\desktop-pets\DesktopRoachRampage\images";
|
||||||
|
|
||||||
|
static void DrawPixel(Bitmap bmp, int x, int y, Color color)
|
||||||
|
{
|
||||||
|
if (x >= 0 && x < bmp.Width && y >= 0 && y < bmp.Height)
|
||||||
|
bmp.SetPixel(x, y, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawThickLeg(Bitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, Color color, Color colorDark)
|
||||||
|
{
|
||||||
|
// 第一段 - 3像素宽
|
||||||
|
DrawPixel(bmp, x1, y1, color);
|
||||||
|
DrawPixel(bmp, x1 - 1, y1, color);
|
||||||
|
DrawPixel(bmp, x1 + 1, y1, color);
|
||||||
|
DrawPixel(bmp, x1, y1 - 1, color);
|
||||||
|
DrawPixel(bmp, x1, y1 + 1, color);
|
||||||
|
|
||||||
|
// 第二段 - 3像素宽
|
||||||
|
DrawPixel(bmp, x2, y2, color);
|
||||||
|
DrawPixel(bmp, x2 - 1, y2, color);
|
||||||
|
DrawPixel(bmp, x2 + 1, y2, color);
|
||||||
|
DrawPixel(bmp, x2, y2 - 1, color);
|
||||||
|
DrawPixel(bmp, x2, y2 + 1, color);
|
||||||
|
|
||||||
|
// 第三段末端(深色)- 略窄
|
||||||
|
DrawPixel(bmp, x3, y3, colorDark);
|
||||||
|
DrawPixel(bmp, x3 - 1, y3, colorDark);
|
||||||
|
DrawPixel(bmp, x3 + 1, y3, colorDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawCockroachBody(Bitmap bmp, int centerX, int centerY, int frame, int totalFrames, bool isFlying, bool isScared = false, bool isCrawling = false)
|
||||||
|
{
|
||||||
|
// 蟑螂颜色
|
||||||
|
var colorHead = Color.FromArgb(100, 65, 35);
|
||||||
|
var colorBody = Color.FromArgb(80, 50, 25);
|
||||||
|
var colorBodyDark = Color.FromArgb(55, 30, 10);
|
||||||
|
var colorLeg = Color.FromArgb(110, 75, 45);
|
||||||
|
var colorLegDark = Color.FromArgb(85, 55, 30);
|
||||||
|
var colorAnt = Color.FromArgb(130, 90, 60);
|
||||||
|
var colorWing = Color.FromArgb(160, 130, 100);
|
||||||
|
var colorWingLight = Color.FromArgb(200, 170, 140);
|
||||||
|
var colorWingVein = Color.FromArgb(120, 90, 60);
|
||||||
|
|
||||||
|
int cy = centerY;
|
||||||
|
|
||||||
|
// 受惊吓时身体略抬高
|
||||||
|
int bodyOffsetY = isScared ? -3 : (isCrawling ? 2 : 0);
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 身体主体
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// 头部
|
||||||
|
DrawPixel(bmp, centerX - 1, cy - 14 + bodyOffsetY, colorHead);
|
||||||
|
DrawPixel(bmp, centerX, cy - 14 + bodyOffsetY, colorHead);
|
||||||
|
DrawPixel(bmp, centerX + 1, cy - 14 + bodyOffsetY, colorHead);
|
||||||
|
|
||||||
|
// 头部区域
|
||||||
|
for (int y = -13; y <= -10; y++) {
|
||||||
|
int width = (y + 13) * 2 + 3;
|
||||||
|
for (int x = -width / 2; x <= width / 2; x++) {
|
||||||
|
DrawPixel(bmp, centerX + x, cy + y + bodyOffsetY, colorHead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前胸区域
|
||||||
|
for (int y = -9; y <= -5; y++) {
|
||||||
|
int width = 9 + (y + 9) * 2;
|
||||||
|
for (int x = -width / 2; x <= width / 2; x++) {
|
||||||
|
DrawPixel(bmp, centerX + x, cy + y + bodyOffsetY, colorBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 腹部主体
|
||||||
|
for (int y = -4; y <= 8; y++) {
|
||||||
|
for (int x = -8; x <= 8; x++) {
|
||||||
|
DrawPixel(bmp, centerX + x, cy + y + bodyOffsetY, colorBodyDark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 腹部后段
|
||||||
|
for (int y = 9; y <= 12; y++) {
|
||||||
|
int width = 17 - (y - 9) * 2;
|
||||||
|
for (int x = -width / 2; x <= width / 2; x++) {
|
||||||
|
DrawPixel(bmp, centerX + x, cy + y + bodyOffsetY, colorBodyDark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尾部末端
|
||||||
|
for (int x = -3; x <= 3; x++)
|
||||||
|
DrawPixel(bmp, centerX + x, cy + 13 + bodyOffsetY, colorBodyDark);
|
||||||
|
for (int x = -2; x <= 2; x++)
|
||||||
|
DrawPixel(bmp, centerX + x, cy + 14 + bodyOffsetY, colorBodyDark);
|
||||||
|
|
||||||
|
// 身体中线纹理
|
||||||
|
for (int y = -10; y <= 12; y++) {
|
||||||
|
DrawPixel(bmp, centerX, cy + y + bodyOffsetY, colorBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 翅膀
|
||||||
|
// ==========================================
|
||||||
|
if (isFlying)
|
||||||
|
{
|
||||||
|
double wingPhase = (double)frame / totalFrames * Math.PI * 2;
|
||||||
|
int wingSpread = (int)(Math.Sin(wingPhase) * 4) + 8;
|
||||||
|
int wingAngle = (int)(Math.Sin(wingPhase * 2) * 2);
|
||||||
|
|
||||||
|
// 左翅膀
|
||||||
|
for (int i = 1; i <= wingSpread; i++) {
|
||||||
|
for (int j = -3; j <= 7; j++) {
|
||||||
|
int yOffset = j + wingAngle;
|
||||||
|
if (i > wingSpread - 2) {
|
||||||
|
DrawPixel(bmp, centerX - 8 - i, cy + yOffset - 4 + bodyOffsetY, colorWingLight);
|
||||||
|
} else {
|
||||||
|
DrawPixel(bmp, centerX - 8 - i, cy + yOffset - 4 + bodyOffsetY, colorWing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右翅膀
|
||||||
|
for (int i = 1; i <= wingSpread; i++) {
|
||||||
|
for (int j = -3; j <= 7; j++) {
|
||||||
|
int yOffset = j - wingAngle;
|
||||||
|
if (i > wingSpread - 2) {
|
||||||
|
DrawPixel(bmp, centerX + 8 + i, cy + yOffset - 4 + bodyOffsetY, colorWingLight);
|
||||||
|
} else {
|
||||||
|
DrawPixel(bmp, centerX + 8 + i, cy + yOffset - 4 + bodyOffsetY, colorWing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 翅膀纹理
|
||||||
|
for (int i = 2; i <= wingSpread - 2; i += 3) {
|
||||||
|
for (int j = 0; j <= 4; j++) {
|
||||||
|
DrawPixel(bmp, centerX - 8 - i, cy + j - 3 + bodyOffsetY, colorWingVein);
|
||||||
|
DrawPixel(bmp, centerX + 8 + i, cy + j - 3 + bodyOffsetY, colorWingVein);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 6条腿 - 非常粗壮
|
||||||
|
// ==========================================
|
||||||
|
int[] legPosY = { cy - 8 + bodyOffsetY, cy + bodyOffsetY, cy + 10 + bodyOffsetY };
|
||||||
|
|
||||||
|
double phase = (double)frame / totalFrames * Math.PI * 2;
|
||||||
|
|
||||||
|
if (isFlying)
|
||||||
|
{
|
||||||
|
// 飞行时腿悬空摆动
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
double legPhase = phase + i * 0.8;
|
||||||
|
int swingX = (int)(Math.Sin(legPhase) * 4);
|
||||||
|
int swingY = (int)(Math.Cos(legPhase) * 2);
|
||||||
|
|
||||||
|
// 左腿
|
||||||
|
DrawThickLeg(bmp,
|
||||||
|
centerX - 8 + swingX, legPosY[i] + swingY,
|
||||||
|
centerX - 11 + swingX, legPosY[i] + 2 + swingY,
|
||||||
|
centerX - 14 + swingX, legPosY[i] + swingY,
|
||||||
|
colorLeg, colorLegDark);
|
||||||
|
|
||||||
|
// 右腿
|
||||||
|
DrawThickLeg(bmp,
|
||||||
|
centerX + 8 - swingX, legPosY[i] + swingY,
|
||||||
|
centerX + 11 - swingX, legPosY[i] + 2 + swingY,
|
||||||
|
centerX + 14 - swingX, legPosY[i] + swingY,
|
||||||
|
colorLeg, colorLegDark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 走路、爬行、受惊吓时的腿
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
double legPhase = phase + i * 0.8;
|
||||||
|
int swing = (int)(Math.Sin(legPhase) * 2);
|
||||||
|
|
||||||
|
// 爬行时腿的位置更低
|
||||||
|
int crawlOffset = isCrawling ? 2 : 0;
|
||||||
|
int scaredOffset = isScared ? -1 : 0;
|
||||||
|
|
||||||
|
// 左腿
|
||||||
|
DrawThickLeg(bmp,
|
||||||
|
centerX - 8, legPosY[i] + crawlOffset + scaredOffset,
|
||||||
|
centerX - 11, legPosY[i] + 2 + swing + crawlOffset + scaredOffset,
|
||||||
|
centerX - 14, legPosY[i] + swing + crawlOffset + scaredOffset,
|
||||||
|
colorLeg, colorLegDark);
|
||||||
|
|
||||||
|
// 右腿
|
||||||
|
DrawThickLeg(bmp,
|
||||||
|
centerX + 8, legPosY[i] + crawlOffset + scaredOffset,
|
||||||
|
centerX + 11, legPosY[i] + 2 - swing + crawlOffset + scaredOffset,
|
||||||
|
centerX + 14, legPosY[i] - swing + crawlOffset + scaredOffset,
|
||||||
|
colorLeg, colorLegDark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 触角
|
||||||
|
// ==========================================
|
||||||
|
double antPhase = phase * 0.5;
|
||||||
|
int antSwingL = (int)(Math.Sin(antPhase) * 2);
|
||||||
|
int antSwingR = (int)(Math.Sin(antPhase + Math.PI) * 2);
|
||||||
|
|
||||||
|
// 受惊吓时触角竖起
|
||||||
|
int antHeight = isScared ? -2 : 0;
|
||||||
|
|
||||||
|
// 左触角 - 加粗
|
||||||
|
for (int i = 1; i <= 7; i++) {
|
||||||
|
DrawPixel(bmp, centerX - 3 - i * 2, cy - 15 - i + antSwingL + bodyOffsetY + antHeight, colorAnt);
|
||||||
|
if (i > 2) {
|
||||||
|
DrawPixel(bmp, centerX - 3 - i * 2 + 1, cy - 15 - i + antSwingL + bodyOffsetY + antHeight, colorAnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右触角 - 加粗
|
||||||
|
for (int i = 1; i <= 7; i++) {
|
||||||
|
DrawPixel(bmp, centerX + 3 + i * 2, cy - 15 - i + antSwingR + bodyOffsetY + antHeight, colorAnt);
|
||||||
|
if (i > 2) {
|
||||||
|
DrawPixel(bmp, centerX + 3 + i * 2 - 1, cy - 15 - i + antSwingR + bodyOffsetY + antHeight, colorAnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CreateCockroachImage(string filename, int frame, int totalFrames, bool isFlying, bool isScared = false, bool isCrawling = false)
|
||||||
|
{
|
||||||
|
int size = 64;
|
||||||
|
using (var bmp = new Bitmap(size, size, PixelFormat.Format32bppArgb))
|
||||||
|
{
|
||||||
|
// 清空为透明
|
||||||
|
for (int y = 0; y < size; y++)
|
||||||
|
for (int x = 0; x < size; x++)
|
||||||
|
bmp.SetPixel(x, y, Color.Transparent);
|
||||||
|
|
||||||
|
int centerX = size / 2;
|
||||||
|
int centerY = size / 2 + 5;
|
||||||
|
|
||||||
|
DrawCockroachBody(bmp, centerX, centerY, frame, totalFrames, isFlying, isScared, isCrawling);
|
||||||
|
|
||||||
|
bmp.Save(Path.Combine(saveDir, filename), ImageFormat.Png);
|
||||||
|
}
|
||||||
|
Console.WriteLine("Saved: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(saveDir))
|
||||||
|
Directory.CreateDirectory(saveDir);
|
||||||
|
|
||||||
|
Console.WriteLine("正在生成像素风格蟑螂动画帧...");
|
||||||
|
|
||||||
|
// 生成8个行走动画帧
|
||||||
|
int walkFrames = 8;
|
||||||
|
for (int i = 0; i < walkFrames; i++) {
|
||||||
|
CreateCockroachImage(string.Format("walk_{0:D2}.png", i), i, walkFrames, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成8个飞行动画帧
|
||||||
|
int flyFrames = 8;
|
||||||
|
for (int i = 0; i < flyFrames; i++) {
|
||||||
|
CreateCockroachImage(string.Format("fly_{0:D2}.png", i), i, flyFrames, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空闲帧
|
||||||
|
CreateCockroachImage("cockroach_idle.png", 0, walkFrames, false);
|
||||||
|
|
||||||
|
// 单独的动作帧 - 腿部加粗版本
|
||||||
|
CreateCockroachImage("cockroach_walk1.png", 0, 4, false);
|
||||||
|
CreateCockroachImage("cockroach_walk2.png", 2, 4, false);
|
||||||
|
CreateCockroachImage("cockroach_crawl.png", 1, 4, false, false, true); // 爬行状态
|
||||||
|
CreateCockroachImage("cockroach_scared.png", 0, 4, false, true, false); // 受惊吓状态
|
||||||
|
|
||||||
|
Console.WriteLine("所有图片生成完成!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import urllib2
|
||||||
|
import urllib
|
||||||
|
import os
|
||||||
|
|
||||||
|
# create save directory
|
||||||
|
save_dir = r"e:\Temp\desktop-pets\images"
|
||||||
|
if not os.path.exists(save_dir):
|
||||||
|
os.makedirs(save_dir)
|
||||||
|
|
||||||
|
# image API base URL
|
||||||
|
base_url = "https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image"
|
||||||
|
|
||||||
|
# cockroach action list
|
||||||
|
cockroach_actions = [
|
||||||
|
("cockroach_idle.png", "pixel art cockroach standing still, cute pixel style, transparent background, 64x64 pixels, dark brown color, detailed antennae, cartoon style"),
|
||||||
|
("cockroach_walk1.png", "pixel art cockroach walking pose 1, cute pixel style, transparent background, 64x64 pixels, dark brown color, legs in motion, cartoon style"),
|
||||||
|
("cockroach_walk2.png", "pixel art cockroach walking pose 2, cute pixel style, transparent background, 64x64 pixels, dark brown color, legs in different position, cartoon style"),
|
||||||
|
("cockroach_crawl.png", "pixel art cockroach crawling low, cute pixel style, transparent background, 64x64 pixels, dark brown color, cartoon style"),
|
||||||
|
("cockroach_scared.png", "pixel art cockroach scared pose, antennae raised, cute pixel style, transparent background, 64x64 pixels, dark brown color, cartoon style")
|
||||||
|
]
|
||||||
|
|
||||||
|
# download each image
|
||||||
|
for filename, prompt in cockroach_actions:
|
||||||
|
print("Generating: " + filename)
|
||||||
|
url = base_url + "?prompt=" + urllib.quote(prompt) + "&image_size=square_hd"
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = urllib2.urlopen(url, timeout=60)
|
||||||
|
image_data = response.read()
|
||||||
|
|
||||||
|
# save image
|
||||||
|
save_path = os.path.join(save_dir, filename)
|
||||||
|
with open(save_path, 'wb') as f:
|
||||||
|
f.write(image_data)
|
||||||
|
print("Saved: " + save_path)
|
||||||
|
except Exception as e:
|
||||||
|
print("Failed to generate " + filename + ": " + str(e))
|
||||||
|
|
||||||
|
print("All images generated!")
|
||||||