render the spritesheet spec in the render system
	
		
			
	
		
	
	
		
			
				
	
				continuous-integration/drone/pr Build is failing
				
					Details
				
			
		
	
				
					
				
			
				
	
				continuous-integration/drone/pr Build is failing
				
					Details
				
			
		
	This commit is contained in:
		
							parent
							
								
									f7e7121fce
								
							
						
					
					
						commit
						8ac11eda05
					
				|  | @ -18,6 +18,19 @@ class SpriteSpec: | |||
|         self.end_x = end_x | ||||
|         self.end_y = end_y | ||||
| 
 | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             "ms_per_frame": self.ms_per_frame, | ||||
|             "top_x": self.top_x, | ||||
|             "top_y": self.top_y, | ||||
|             "end_x": self.end_x, | ||||
|             "end_y": self.end_y, | ||||
|             "frames": self.frames, | ||||
|         } | ||||
| 
 | ||||
|     def __repr__(self) -> str: | ||||
|         return f"SpriteSpec(ms_per_frame={self.ms_per_frame}, top_x={self.top_x}, top_y={self.top_y}, end_x={self.end_x}, end_y={self.end_y}, frames={self.frames})" | ||||
| 
 | ||||
| 
 | ||||
| class SpriteSheet(Component): | ||||
|     def __init__( | ||||
|  | @ -44,5 +57,7 @@ class SpriteSheet(Component): | |||
|             "current_frame": self.current_frame, | ||||
|             "last_update": self.last_update, | ||||
|             "current_state": self.initial_state, | ||||
|             "state_to_spritespec": self.state_to_spritespec, | ||||
|             "state_to_spritespec": { | ||||
|                 k: v.to_dict() for k, v in self.state_to_spritespec.items() | ||||
|             }, | ||||
|         } | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ from enum import Enum | |||
| from .entity import Entity, EntityType | ||||
| 
 | ||||
| 
 | ||||
| class CatState(Enum): | ||||
| class CatState(str, Enum): | ||||
|     IDLE = "IDLE" | ||||
|     FROLICKING = "FROLICKING" | ||||
|     EEPY = "EEPY" | ||||
|  | @ -13,20 +13,47 @@ class CatState(Enum): | |||
|     CHASING_CAT = "CHASING_CAT" | ||||
|     SCRATCHING = "SCRATCHING" | ||||
|     ITCHY = "ITCHY" | ||||
|     MAKING_BISCUITS = "MAKING_BISCUITS" | ||||
| 
 | ||||
| 
 | ||||
| class CatSpriteState(str, Enum): | ||||
|     ALERT = "ALERT" | ||||
|     MAKING_BISCUITS = "MAKING_BISCUITS" | ||||
| 
 | ||||
| 
 | ||||
| class Cat(Entity): | ||||
|     def __init__(self, id: str, spritesheet_source: str): | ||||
|         state_to_spritespec = self.get_state_to_spritespec() | ||||
|         components = [ | ||||
|             Position(0, 0), | ||||
|             SpriteSheet(spritesheet_source, state_to_spritespec, CatState.ALERT), | ||||
|             Position(50, 50), | ||||
|             SpriteSheet( | ||||
|                 spritesheet_source, state_to_spritespec, CatSpriteState.MAKING_BISCUITS | ||||
|             ), | ||||
|         ] | ||||
| 
 | ||||
|         super().__init__(EntityType.CAT, id, components) | ||||
| 
 | ||||
|     def get_state_to_spritespec(self): | ||||
|         return {CatState.ALERT: SpriteSpec(100, 0, 0, 200, 40, 5)} | ||||
|         creature_width = 32 | ||||
|         creature_height = 32 | ||||
|         return { | ||||
|             CatSpriteState.ALERT: SpriteSpec( | ||||
|                 100, | ||||
|                 creature_width * 7, | ||||
|                 creature_height * 3, | ||||
|                 creature_width * 8, | ||||
|                 creature_height * 4, | ||||
|                 1, | ||||
|             ), | ||||
|             CatSpriteState.MAKING_BISCUITS: SpriteSpec( | ||||
|                 300, | ||||
|                 0, | ||||
|                 0, | ||||
|                 creature_width, | ||||
|                 creature_height * 2, | ||||
|                 2, | ||||
|             ), | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ export interface SpriteSpec { | |||
| export interface SpriteSheetComponent extends Component { | ||||
|   name: ComponentType.SPRITESHEET; | ||||
|   source: string; | ||||
|   sheet: HTMLImageElement; | ||||
|   sheet?: HTMLImageElement; | ||||
| 
 | ||||
|   current_frame: number; | ||||
|   last_update: number; | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ export class DebouncePublisher<T> { | |||
| 
 | ||||
|   constructor( | ||||
|     private readonly publisher: (data: T) => void | Promise<void>, | ||||
|     private readonly debounce_ms = 150, | ||||
|     private readonly debounce_ms = 100, | ||||
|   ) {} | ||||
| 
 | ||||
|   public start() { | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ import { | |||
|   ComponentType, | ||||
|   PositionComponent, | ||||
|   TrailingPositionComponent, | ||||
|   SpriteSheetComponent, | ||||
|   SpriteSpec, | ||||
| } from "./component"; | ||||
| import { Game } from "./game"; | ||||
| import { System, SystemType } from "./system"; | ||||
|  | @ -17,7 +19,7 @@ export class RenderSystem extends System { | |||
|     this.canvas.height = height; | ||||
|   } | ||||
| 
 | ||||
|   public update(_dt: number, game: Game) { | ||||
|   public update(dt: number, game: Game) { | ||||
|     const ctx = this.canvas.getContext("2d"); | ||||
|     if (ctx === null) return; | ||||
|     ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | ||||
|  | @ -32,15 +34,87 @@ export class RenderSystem extends System { | |||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (ComponentType.POSITION in entity.components) { | ||||
|       if ( | ||||
|         ComponentType.POSITION in entity.components && | ||||
|         ComponentType.SPRITESHEET in entity.components | ||||
|       ) { | ||||
|         const position = entity.components[ | ||||
|           ComponentType.POSITION | ||||
|         ] as PositionComponent; | ||||
|         ctx.beginPath(); | ||||
|         ctx.arc(position.x, position.y, 50, 0, 2 * Math.PI); | ||||
|         ctx.stroke(); | ||||
|         const spritesheet = entity.components[ | ||||
|           ComponentType.SPRITESHEET | ||||
|         ] as SpriteSheetComponent; | ||||
| 
 | ||||
|         if (typeof spritesheet.sheet === "undefined") { | ||||
|           const img = new Image(); | ||||
|           img.src = spritesheet.source; | ||||
|           spritesheet.sheet = img; | ||||
|         } | ||||
|         const spritespec = | ||||
|           spritesheet.state_to_spritespec[spritesheet.current_state]; | ||||
| 
 | ||||
|         this.blit_sprite(ctx, spritesheet, position); | ||||
| 
 | ||||
|         spritesheet.last_update += dt; | ||||
|         if (spritesheet.last_update > spritespec.ms_per_frame) { | ||||
|           spritesheet.current_frame++; | ||||
|           spritesheet.current_frame %= spritespec.frames; | ||||
|           spritesheet.last_update = 0; | ||||
|         } | ||||
| 
 | ||||
|         return; | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   private blit_sprite( | ||||
|     ctx: CanvasRenderingContext2D, | ||||
|     spritesheet: SpriteSheetComponent, | ||||
|     position: { x: number; y: number }, | ||||
|   ) { | ||||
|     ctx.save(); | ||||
|     ctx.translate(position.x, position.y); | ||||
|     ctx.translate(-position.x, -position.y); | ||||
| 
 | ||||
|     if (typeof spritesheet.sheet === "undefined") return; | ||||
| 
 | ||||
|     const spritespec = | ||||
|       spritesheet.state_to_spritespec[spritesheet.current_state]; | ||||
| 
 | ||||
|     ctx.drawImage( | ||||
|       spritesheet.sheet, | ||||
|       ...this.get_sprite_args(spritesheet.current_frame, spritespec), | ||||
|       ...this.get_draw_args(spritespec, position), | ||||
|     ); | ||||
| 
 | ||||
|     ctx.restore(); | ||||
|   } | ||||
| 
 | ||||
|   private get_sprite_args( | ||||
|     current_frame: number, | ||||
|     sprite_spec: SpriteSpec, | ||||
|   ): [sx: number, sy: number, sw: number, sh: number] { | ||||
|     const [width, height] = this.get_dimensions(sprite_spec); | ||||
|     return [ | ||||
|       sprite_spec.top_x, | ||||
|       sprite_spec.top_y + current_frame * height, | ||||
|       width, | ||||
|       height, | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   private get_draw_args( | ||||
|     sprite_spec: SpriteSpec, | ||||
|     position: { x: number; y: number }, | ||||
|   ): [dx: number, dy: number, dw: number, dh: number] { | ||||
|     const [width, height] = this.get_dimensions(sprite_spec); | ||||
|     return [position.x - width / 2, position.y - height / 2, width, height]; | ||||
|   } | ||||
| 
 | ||||
|   private get_dimensions(sprite_spec: SpriteSpec): [number, number] { | ||||
|     return [ | ||||
|       sprite_spec.end_x - sprite_spec.top_x, | ||||
|       (sprite_spec.end_y - sprite_spec.top_y) / sprite_spec.frames, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -2,19 +2,15 @@ import { ComponentType, TrailingPositionComponent } from "./component"; | |||
| import { Game } from "./game"; | ||||
| import { System, SystemType } from "./system"; | ||||
| 
 | ||||
| interface Point { | ||||
|   x: number; | ||||
|   y: number; | ||||
|   time: number; | ||||
| } | ||||
| 
 | ||||
| export class TrailingPositionSystem extends System { | ||||
|   constructor( | ||||
|     private readonly point_filter: ( | ||||
|       trail_point: { | ||||
|         x: number; | ||||
|         y: number; | ||||
|         time: number; | ||||
|       }[], | ||||
|     ) => { | ||||
|       x: number; | ||||
|       y: number; | ||||
|       time: number; | ||||
|     }[], | ||||
|     private readonly point_filter: (trail_point: Array<Point>) => Array<Point> | ||||
|   ) { | ||||
|     super(SystemType.TRAILING_POSITION); | ||||
|   } | ||||
|  | @ -27,7 +23,7 @@ export class TrailingPositionSystem extends System { | |||
|           ComponentType.TRAILING_POSITION | ||||
|         ] as TrailingPositionComponent; | ||||
|         trailing_position.trails = this.point_filter(trailing_position.trails); | ||||
|       }, | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue