Newer
Older
DungeonShooting / DungeonShooting_Godot / src / test / TestMask2.cs
@小李xl 小李xl on 27 Nov 2023 14 KB 液体边缘平滑效果
  1. using Godot;
  2. using System;
  3. using System.Collections.Generic;
  4.  
  5. public partial class TestMask2 : SubViewportContainer
  6. {
  7. public class ImageData
  8. {
  9. public int Width;
  10. public int Height;
  11. public PixelData[] Pixels;
  12.  
  13. //有效像素范围
  14. public int PixelMinX = int.MaxValue;
  15. public int PixelMinY = int .MaxValue;
  16. public int PixelMaxX;
  17. public int PixelMaxY;
  18.  
  19. public int PixelWidth;
  20. public int PixelHeight;
  21.  
  22. //补帧间距倍率
  23. public float Ffm;
  24. public ImageData(Image image, byte type, float ffm, float duration, float writeOffSpeed)
  25. {
  26. Ffm = ffm;
  27. var list = new List<PixelData>();
  28. var width = image.GetWidth();
  29. var height = image.GetHeight();
  30. var flag = false;
  31. for (var x = 0; x < width; x++)
  32. {
  33. for (var y = 0; y < height; y++)
  34. {
  35. var pixel = image.GetPixel(x, y);
  36. if (pixel.A > 0)
  37. {
  38. flag = true;
  39. list.Add(new PixelData()
  40. {
  41. X = x,
  42. Y = y,
  43. Color = pixel,
  44. Type = type,
  45. Duration = duration,
  46. WriteOffSpeed = writeOffSpeed
  47. });
  48. if (x < PixelMinX)
  49. {
  50. PixelMinX = x;
  51. }
  52. else if (x > PixelMaxX)
  53. {
  54. PixelMaxX = x;
  55. }
  56. if (y < PixelMinY)
  57. {
  58. PixelMinY = y;
  59. }
  60. else if (y > PixelMaxY)
  61. {
  62. PixelMaxY = y;
  63. }
  64. }
  65. }
  66. }
  67.  
  68. if (!flag)
  69. {
  70. throw new Exception("不能使用完全透明的图片作为笔刷!");
  71. }
  72. Pixels = list.ToArray();
  73. Width = width;
  74. Height = height;
  75.  
  76. PixelWidth = PixelMaxX - PixelMinX;
  77. PixelHeight = PixelMaxY - PixelMinY;
  78. }
  79. }
  80. public class PixelData
  81. {
  82. public int X;
  83. public int Y;
  84. public Color Color;
  85. public byte Type;
  86. public float Duration;
  87. public float WriteOffSpeed;
  88. }
  89.  
  90. public class ImagePixel
  91. {
  92. public int X;
  93. public int Y;
  94. public Color Color;
  95. public byte Type;
  96. public float Timer;
  97. public float Speed;
  98. public bool IsRun;
  99. public float TempTime;
  100. public bool TempFlag;
  101. }
  102. [Export]
  103. public Sprite2D Canvas;
  104.  
  105. [Export]
  106. public Texture2D Brush1;
  107. [Export]
  108. public Texture2D Brush2;
  109.  
  110. private ImageData _brushData1;
  111. private ImageData _brushData2;
  112. private Image _image;
  113. private ImageTexture _texture;
  114.  
  115. private ImagePixel[,] _imagePixels;
  116. private List<ImagePixel> _cacheImagePixels = new List<ImagePixel>();
  117. private float _runTime = 0;
  118. private int _executeIndex = -1;
  119. private Vector2I? _prevPosition = null;
  120. private List<ImagePixel> _tempList = new List<ImagePixel>();
  121. private int PixelScale;
  122.  
  123. //程序每帧最多等待执行时间, 超过这个时间的像素点将交到下一帧执行, 单位: 毫秒
  124. private float _maxWaitTime = 4f;
  125. public override void _Ready()
  126. {
  127. Engine.MaxFps = (int)DisplayServer.ScreenGetRefreshRate();
  128. //Engine.MaxFps = 5;
  129. _brushData1 = new ImageData(Brush1.GetImage(), 1, 0.5f, 5, 0.1f);
  130. _brushData2 = new ImageData(Brush2.GetImage(), 2, 0.5f, 5, 0.05f);
  131. var canvasScale = 4;
  132. var width = (int)(Size.X / canvasScale);
  133. var height = (int)(Size.Y / canvasScale);
  134. PixelScale = (int)(Scale.X * canvasScale);
  135. _image = Image.Create(width, height, false, Image.Format.Rgba8);
  136. _texture = ImageTexture.CreateFromImage(_image);
  137. Canvas.Texture = _texture;
  138. _imagePixels = new ImagePixel[width, height];
  139. Debug.Log("width: : " + _brushData2.PixelWidth + ", height: " + _brushData2.PixelHeight);
  140. }
  141.  
  142. public override void _Process(double delta)
  143. {
  144. //更新消除逻辑
  145. if (_cacheImagePixels.Count > 0)
  146. {
  147. var startIndex = _executeIndex;
  148. if (_executeIndex < 0 || _executeIndex >= _cacheImagePixels.Count)
  149. {
  150. _executeIndex = _cacheImagePixels.Count - 1;
  151. }
  152.  
  153. var startTime = DateTime.UtcNow;
  154. var isOver = false;
  155. var index = 0;
  156. for (; _executeIndex >= 0; _executeIndex--)
  157. {
  158. index++;
  159. var imagePixel = _cacheImagePixels[_executeIndex];
  160. if (UpdateImagePixel(imagePixel)) //移除
  161. {
  162. _cacheImagePixels.RemoveAt(_executeIndex);
  163. if (_executeIndex < startIndex)
  164. {
  165. startIndex--;
  166. }
  167. }
  168.  
  169. if (index > 200)
  170. {
  171. index = 0;
  172. if ((DateTime.UtcNow - startTime).TotalMilliseconds > _maxWaitTime) //超过最大执行时间
  173. {
  174. isOver = true;
  175. break;
  176. }
  177. }
  178. }
  179.  
  180. if (!isOver && startIndex >= 0 && _executeIndex < 0)
  181. {
  182. _executeIndex = _cacheImagePixels.Count - 1;
  183. for (; _executeIndex >= startIndex; _executeIndex--)
  184. {
  185. index++;
  186. var imagePixel = _cacheImagePixels[_executeIndex];
  187. if (UpdateImagePixel(imagePixel)) //移除
  188. {
  189. _cacheImagePixels.RemoveAt(_executeIndex);
  190. }
  191. if (index > 200)
  192. {
  193. index = 0;
  194. if ((DateTime.UtcNow - startTime).TotalMilliseconds > _maxWaitTime) //超过最大执行时间
  195. {
  196. break;
  197. }
  198. }
  199. }
  200. }
  201. }
  202. var pos = (GetGlobalMousePosition() / PixelScale).AsVector2I();
  203. if (Input.IsMouseButtonPressed(MouseButton.Left)) //绘制画笔1
  204. {
  205. var time = DateTime.UtcNow;
  206. // if (_prevPosition != null)
  207. // {
  208. // DrawBrush(_brushData1, _prevPosition, pos, new Vector2(pos.X - _prevPosition.Value.X, pos.Y - _prevPosition.Value.Y).Angle());
  209. // }
  210. // else
  211. {
  212. DrawBrush(_brushData1, _prevPosition, pos, 0);
  213. }
  214. _prevPosition = pos;
  215. Debug.Log("用时: " + (DateTime.UtcNow - time).TotalMilliseconds);
  216. }
  217. else if (Input.IsMouseButtonPressed(MouseButton.Right)) //绘制画笔2
  218. {
  219. var time = DateTime.UtcNow;
  220. // if (_prevPosition != null)
  221. // {
  222. // DrawBrush(_brushData2, _prevPosition, pos, new Vector2(pos.X - _prevPosition.Value.X, pos.Y - _prevPosition.Value.Y).Angle());
  223. // }
  224. // else
  225. {
  226. DrawBrush(_brushData2, _prevPosition, pos, 0);
  227. }
  228. _prevPosition = pos;
  229. Debug.Log("用时: " + (DateTime.UtcNow - time).TotalMilliseconds);
  230. }
  231. else
  232. {
  233. _prevPosition = null;
  234. }
  235.  
  236. //碰撞检测
  237. if (Input.IsKeyPressed(Key.Space))
  238. {
  239. var mousePosition = GetGlobalMousePosition();
  240. var pixel = _image.GetPixel((int)mousePosition.X / PixelScale, (int)mousePosition.Y / PixelScale);
  241. Debug.Log("是否碰撞: " + (pixel.A > 0));
  242. }
  243. _texture.Update(_image);
  244. _runTime += (float)delta;
  245. }
  246.  
  247. private bool UpdateImagePixel(ImagePixel imagePixel)
  248. {
  249. if (imagePixel.Color.A > 0)
  250. {
  251. if (imagePixel.Timer > 0)
  252. {
  253. imagePixel.Timer -= _runTime - imagePixel.TempTime;
  254. imagePixel.TempTime = _runTime;
  255. }
  256. else
  257. {
  258. imagePixel.Color.A -= imagePixel.Speed * (_runTime - imagePixel.TempTime);
  259. if (imagePixel.Color.A <= 0)
  260. {
  261. _image.SetPixel(imagePixel.X, imagePixel.Y, new Color(0, 0, 0, 0));
  262. imagePixel.IsRun = false;
  263. return true;
  264. }
  265. else
  266. {
  267. _image.SetPixel(imagePixel.X, imagePixel.Y, imagePixel.Color);
  268. imagePixel.TempTime = _runTime;
  269. }
  270. }
  271. }
  272.  
  273. return false;
  274. }
  275.  
  276. private void DrawBrush(ImageData brush, Vector2I? prevPosition, Vector2I position, float rotation)
  277. {
  278. var center = new Vector2I(brush.Width, brush.Height) / 2;
  279. var pos = position - center;
  280. var canvasWidth = _texture.GetWidth();
  281. var canvasHeight = _texture.GetHeight();
  282. //存在上一次记录的点
  283. if (prevPosition != null)
  284. {
  285. var offset = new Vector2(position.X - prevPosition.Value.X, position.Y - prevPosition.Value.Y);
  286. var maxL = Mathf.Lerp(
  287. brush.PixelHeight,
  288. brush.PixelWidth,
  289. Mathf.Abs(Mathf.Sin(offset.Angle() - rotation + Mathf.Pi * 0.5f))
  290. ) * brush.Ffm;
  291. var len = offset.Length();
  292. if (len > maxL) //距离太大了, 需要补间
  293. {
  294. Debug.Log($"距离太大了, 启用补间: len: {len}, maxL: {maxL}");
  295. var count = Mathf.CeilToInt(len / maxL);
  296. var step = new Vector2(offset.X / count, offset.Y / count);
  297. var prevPos = prevPosition.Value - center;
  298. for (var i = 1; i <= count; i++)
  299. {
  300. foreach (var brushPixel in brush.Pixels)
  301. {
  302. var brushPos = RotatePixels(brushPixel.X, brushPixel.Y, center.X, center.Y, rotation);
  303. var x = (int)(prevPos.X + step.X * i + brushPos.X);
  304. var y = (int)(prevPos.Y + step.Y * i + brushPos.Y);
  305. if (x >= 0 && x < canvasWidth && y >= 0 && y < canvasHeight)
  306. {
  307. var temp = SetPixelData(x, y, brushPixel);
  308. if (!temp.TempFlag)
  309. {
  310. temp.TempFlag = true;
  311. _tempList.Add(temp);
  312. }
  313. }
  314. }
  315. }
  316. foreach (var brushPixel in brush.Pixels)
  317. {
  318. var brushPos = RotatePixels(brushPixel.X, brushPixel.Y, center.X, center.Y, rotation);
  319. var x = pos.X + brushPos.X;
  320. var y = pos.Y + brushPos.Y;
  321. if (x >= 0 && x < canvasWidth && y >= 0 && y < canvasHeight)
  322. {
  323. var temp = SetPixelData(x, y, brushPixel);
  324. if (!temp.TempFlag)
  325. {
  326. temp.TempFlag = true;
  327. _tempList.Add(temp);
  328. }
  329. }
  330. }
  331.  
  332. foreach (var imagePixel in _tempList)
  333. {
  334. _image.SetPixel(imagePixel.X, imagePixel.Y, imagePixel.Color);
  335. imagePixel.TempFlag = false;
  336. }
  337.  
  338. _tempList.Clear();
  339. return;
  340. }
  341. }
  342.  
  343. foreach (var brushPixel in brush.Pixels)
  344. {
  345. var brushPos = RotatePixels(brushPixel.X, brushPixel.Y, center.X, center.Y, rotation);
  346. var x = pos.X + brushPos.X;
  347. var y = pos.Y + brushPos.Y;
  348. if (x >= 0 && x < canvasWidth && y >= 0 && y < canvasHeight)
  349. {
  350. var temp = SetPixelData(x, y, brushPixel);
  351. _image.SetPixel(x, y, temp.Color);
  352. }
  353. }
  354. }
  355.  
  356. private ImagePixel SetPixelData(int x, int y, PixelData pixelData)
  357. {
  358. var temp = _imagePixels[x, y];
  359. if (temp == null)
  360. {
  361. temp = new ImagePixel()
  362. {
  363. X = x,
  364. Y = y,
  365. Color = pixelData.Color,
  366. Type = pixelData.Type,
  367. Timer = pixelData.Duration,
  368. Speed = pixelData.WriteOffSpeed,
  369. };
  370. _imagePixels[x, y] = temp;
  371. _cacheImagePixels.Add(temp);
  372. temp.IsRun = true;
  373. temp.TempTime = _runTime;
  374. }
  375. else
  376. {
  377. if (temp.Type != pixelData.Type)
  378. {
  379. temp.Color = pixelData.Color;
  380. temp.Type = pixelData.Type;
  381. }
  382. else
  383. {
  384. var tempColor = pixelData.Color;
  385. temp.Color = new Color(tempColor.R, tempColor.G, tempColor.B, Mathf.Max(temp.Color.A, tempColor.A));
  386. }
  387. temp.Speed = pixelData.WriteOffSpeed;
  388. temp.Timer = pixelData.Duration;
  389. if (!temp.IsRun)
  390. {
  391. _cacheImagePixels.Add(temp);
  392. temp.IsRun = true;
  393. temp.TempTime = _runTime;
  394. }
  395. }
  396.  
  397. return temp;
  398. }
  399.  
  400. /// <summary>
  401. /// 根据 rotation 旋转像素点坐标, 并返回旋转后的坐标, rotation 为弧度制角度, 旋转中心点为 centerX, centerY
  402. /// </summary>
  403. private Vector2I RotatePixels(int x, int y, int centerX, int centerY, float rotation)
  404. {
  405. if (rotation == 0)
  406. {
  407. return new Vector2I(x, y);
  408. }
  409.  
  410. x -= centerX;
  411. y -= centerY;
  412. var sv = Mathf.Sin(rotation);
  413. var cv = Mathf.Cos(rotation);
  414. var newX = Mathf.RoundToInt(x * cv - y * sv);
  415. var newY = Mathf.RoundToInt(x * sv + y * cv);
  416. newX += centerX;
  417. newY += centerY;
  418. return new Vector2I(newX, newY);
  419. }
  420. }