1
0

feat: 初始版本

This commit is contained in:
2026-07-22 20:34:10 +08:00
commit 8859dc0da5
24 changed files with 1065 additions and 0 deletions
+706
View File
@@ -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);
}
}
}