Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / map / image / ImageCanvas.cs
@小李xl 小李xl on 11 Apr 2024 8 KB 更新敌人效果
  1.  
  2. using System;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// 静态图像画布类, 用于处理游戏中大量静态物体的解决方案<br/>
  7. /// 将物体纹理绘直接绘制到画布上, 这样大大减少GPU开销, 从而提高帧率<br/>
  8. /// 图像旋转遵循完美像素
  9. /// </summary>
  10. public partial class ImageCanvas : Sprite2D, IDestroy
  11. {
  12. /// <summary>
  13. /// 画布大小
  14. /// </summary>
  15. public int Width { get; }
  16. /// <summary>
  17. /// 画布高度
  18. /// </summary>
  19. public int Height { get; }
  20.  
  21. public bool IsDestroyed { get; private set; }
  22.  
  23. private Image _canvas;
  24. private ImageTexture _texture;
  25.  
  26. public ImageCanvas(int width, int height)
  27. {
  28. Width = width;
  29. Height = height;
  30.  
  31. _canvas = Image.Create(width, height, false, Image.Format.Rgba8);
  32. _texture = ImageTexture.CreateFromImage(_canvas);
  33. }
  34.  
  35. public override void _Ready()
  36. {
  37. Centered = false;
  38. Texture = _texture;
  39. ZIndex = -1;
  40. }
  41.  
  42. /// <summary>
  43. /// 将指定纹理添加到预渲染队列中, 完成后会调用 onDrawingComplete 回调函数
  44. /// </summary>
  45. /// <param name="texture">需要渲染的纹理</param>
  46. /// <param name="modulate">混色</param>
  47. /// <param name="material">渲染材质, 不需要则传null</param>
  48. /// <param name="x">离画布左上角x坐标</param>
  49. /// <param name="y">离画布左上角y坐标</param>
  50. /// <param name="angle">旋转角度, 角度制</param>
  51. /// <param name="centerX">旋转中心点x</param>
  52. /// <param name="centerY">旋转中心点y</param>
  53. /// <param name="flipY">是否翻转y轴</param>
  54. /// <param name="onDrawingComplete">绘制完成的回调函数</param>
  55. public void DrawImageInCanvas(Texture2D texture, Color modulate, Material material, float x, float y, float angle, int centerX, int centerY, bool flipY, Action onDrawingComplete = null)
  56. {
  57. DrawImageInCanvas(texture,modulate, material, x, y, angle, centerX, centerY, flipY, true, onDrawingComplete);
  58. }
  59. private void DrawImageInCanvas(Texture2D texture, Color modulate, Material material, float x, float y, float angle, int centerX, int centerY, bool flipY, bool enableQueueCutting, Action onDrawingComplete)
  60. {
  61. var item = new ImageRenderData();
  62. item.OnDrawingComplete = onDrawingComplete;
  63. item.EnableQueueCutting = enableQueueCutting;
  64. item.ImageCanvas = this;
  65. item.SrcImage = texture.GetImage();
  66. item.Modulate = modulate;
  67. item.Material = material;
  68. var width = item.SrcImage.GetWidth();
  69. var height = item.SrcImage.GetHeight();
  70. if (width > 128)
  71. {
  72. Debug.LogError("警告: 图像宽度大于 128, 旋转后像素点可能绘制到画布外导致像素丢失!");
  73. }
  74. if (height > 128)
  75. {
  76. Debug.LogError("警告: 图像高度大于 128, 旋转后像素点可能绘制到画布外导致像素丢失!");
  77. }
  78. item.X = Mathf.RoundToInt(x);
  79. item.Y = Mathf.RoundToInt(y);
  80. item.Rotation = Mathf.DegToRad(Mathf.RoundToInt(Utils.ConvertAngle(angle)));
  81. item.CenterX = centerX;
  82. item.CenterY = centerY;
  83. if (item.Rotation > Mathf.Pi)
  84. {
  85. item.CenterX = width - item.CenterX;
  86. item.CenterY = height - item.CenterY;
  87. item.Rotation -= Mathf.Pi;
  88. item.RotationGreaterThanPi = true;
  89. }
  90. item.FlipY = flipY;
  91. var cosAngle = Mathf.Cos(item.Rotation);
  92. var sinAngle = Mathf.Sin(item.Rotation);
  93. if (cosAngle == 0)
  94. {
  95. cosAngle = 1e-6f;
  96. }
  97.  
  98. if (sinAngle == 0)
  99. {
  100. sinAngle = 1e-6f;
  101. }
  102.  
  103. //旋转后的图片宽高
  104. item.RenderWidth = Mathf.CeilToInt(width * Mathf.Abs(cosAngle) + height * sinAngle) + 2;
  105. item.RenderHeight = Mathf.CeilToInt(width * sinAngle + height * Mathf.Abs(cosAngle)) + 2;
  106.  
  107. if (item.RenderWidth >= RenderViewportSize.X)
  108. {
  109. Debug.LogError($"图像旋转后的宽度大于{RenderViewportSize.X}, 不支持绘制到 ImageCanvas 下!");
  110. item.SrcImage.Dispose();
  111. return;
  112. }
  113. //旋转后的图像中心点偏移
  114. item.RenderOffsetX =
  115. (int)((item.CenterX / sinAngle +
  116. (0.5f * item.RenderWidth * sinAngle - 0.5f * item.RenderHeight * cosAngle +
  117. 0.5f * height) /
  118. cosAngle -
  119. (-0.5f * item.RenderWidth * cosAngle - 0.5f * item.RenderHeight * sinAngle +
  120. 0.5f * width) /
  121. sinAngle - item.CenterY / cosAngle) /
  122. (cosAngle / sinAngle + sinAngle / cosAngle)) + 1;
  123. item.RenderOffsetY =
  124. (int)((item.CenterX / cosAngle -
  125. (-0.5f * item.RenderWidth * cosAngle - 0.5f * item.RenderHeight * sinAngle + 0.5f * width) /
  126. cosAngle -
  127. (0.5f * item.RenderWidth * sinAngle - 0.5f * item.RenderHeight * cosAngle + 0.5f * height) /
  128. sinAngle + item.CenterY / sinAngle) /
  129. (sinAngle / cosAngle + cosAngle / sinAngle)) + 1;
  130.  
  131. _queueItems.Add(item);
  132. }
  133. /// <summary>
  134. /// 将指定 ActivityObject 添加到预渲染队列中, 完成后会调用 onDrawingComplete 回调函数
  135. /// </summary>
  136. /// <param name="activityObject">物体实例</param>
  137. /// <param name="x">离画布左上角x坐标</param>
  138. /// <param name="y">离画布左上角y坐标</param>
  139. /// <param name="onDrawingComplete">绘制完成的回调</param>
  140. public void DrawActivityObjectInCanvas(ActivityObject activityObject, float x, float y, Action onDrawingComplete = null)
  141. {
  142. //是否翻转y轴
  143. var flipY = activityObject.Scale.Y < 0;
  144.  
  145. var animatedSprite = activityObject.AnimatedSprite;
  146. var animatedSpritePosition = animatedSprite.Position;
  147. var ax = x + animatedSpritePosition.X;
  148. var ay = y + animatedSpritePosition.X;
  149.  
  150. //先绘制阴影
  151. var shadowSprite = activityObject.ShadowSprite;
  152. var shadowSpriteTexture = activityObject.ShadowSprite.Texture;
  153. if (shadowSpriteTexture != null)
  154. {
  155. var spriteOffset = shadowSprite.Offset;
  156. var centerX = (int)-spriteOffset.X;
  157. var centerY = (int)-spriteOffset.Y;
  158. var angle = Utils.ConvertAngle(shadowSprite.GlobalRotationDegrees);
  159. if (shadowSprite.Centered)
  160. {
  161. centerX += shadowSpriteTexture.GetWidth() / 2;
  162. centerY += shadowSpriteTexture.GetHeight() / 2;
  163. }
  164. DrawImageInCanvas(
  165. shadowSprite.Texture, shadowSprite.Modulate, shadowSprite.Material,
  166. ax + activityObject.ShadowOffset.X, ay + activityObject.ShadowOffset.Y,
  167. angle,
  168. centerX, centerY, flipY,
  169. true, null
  170. );
  171. }
  172. //再绘制纹理
  173. var texture = activityObject.GetCurrentTexture();
  174. if (texture != null)
  175. {
  176. var spriteOffset = animatedSprite.Offset;
  177. var centerX = (int)-spriteOffset.X;
  178. var centerY = (int)-spriteOffset.Y;
  179. if (animatedSprite.Centered)
  180. {
  181. centerX += texture.GetWidth() / 2;
  182. centerY += texture.GetHeight() / 2;
  183. }
  184. //为了保证阴影在此之前渲染, 所以必须关闭插队渲染
  185. DrawImageInCanvas(
  186. texture, animatedSprite.Modulate, animatedSprite.Material,
  187. ax, ay,
  188. animatedSprite.GlobalRotationDegrees,
  189. centerX, centerY, flipY,
  190. false, onDrawingComplete
  191. );
  192. }
  193. }
  194. public void Destroy()
  195. {
  196. if (IsDestroyed)
  197. {
  198. return;
  199. }
  200.  
  201. IsDestroyed = true;
  202. QueueFree();
  203. _canvas.Dispose();
  204. _texture.Dispose();
  205. }
  206.  
  207. /// <summary>
  208. /// 使用透明色替换掉整个画布
  209. /// </summary>
  210. public void Clear()
  211. {
  212. Clear(new Color(0, 0, 0, 0));
  213. }
  214.  
  215. /// <summary>
  216. /// 使用指定颜色替换掉整个画布
  217. /// </summary>
  218. public void Clear(Color color)
  219. {
  220. _canvas.Fill(color);
  221. Redraw();
  222. }
  223. private void Redraw()
  224. {
  225. _texture.Update(_canvas);
  226. }
  227. }