Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4 | daniel-mar | 1 | unit DXMisc; |
2 | |||
3 | // Contains miscellaneous code |
||
4 | |||
5 | interface |
||
6 | |||
7 | uses classes, controls; |
||
8 | |||
9 | type |
||
10 | // This obtains the co-ords for the mouse relative to a control |
||
11 | TMousePos = class(Tobject) |
||
12 | private |
||
13 | { Private declarations } |
||
14 | // Data |
||
15 | fControl: TControl; |
||
16 | |||
17 | fMouseX: integer; |
||
18 | fMouseY: integer; |
||
19 | fXDelta: integer; |
||
20 | fYDelta: integer; |
||
21 | // used to scale the mouse input |
||
22 | fSensitivity: Single; |
||
23 | // Options |
||
24 | fClipToControl: boolean; |
||
25 | protected |
||
26 | { Protected declarations } |
||
27 | procedure SetClipState(Value: boolean); |
||
28 | public |
||
29 | { Public declarations } |
||
30 | constructor Create(Control: TControl); |
||
31 | destructor Destroy; override; |
||
32 | // Sets the control to use |
||
33 | procedure SetControl(AControl: TControl); |
||
34 | // Grabs the latest mouse co-ords |
||
35 | procedure Update; |
||
36 | // Relative to the parent control |
||
37 | property MouseX: integer read fMouseX; |
||
38 | property MouseY: integer read fMouseY; |
||
39 | property XDelta: integer read fXDelta; |
||
40 | property YDelta: integer read fYDelta; |
||
41 | // The sensitivity of the mouse |
||
42 | property Sensitivity: Single read fSensitivity write fSensitivity; |
||
43 | property ClipToControl: Boolean read fClipToControl write SetClipState; |
||
44 | |||
45 | property Control: TControl read fControl write SetControl; |
||
46 | published |
||
47 | { Published declarations } |
||
48 | end; |
||
49 | |||
50 | // This is an example of how to cap the frame rate |
||
51 | // this has NOT been optimized and is just to hide the relatively |
||
52 | // mess code needed to properly calc the time delta for a frame |
||
53 | // and the time delta since the last render |
||
54 | TTiming = class(TObject) |
||
55 | protected |
||
56 | { Protected declarations } |
||
57 | // Used to implement frame capping |
||
58 | fGraphicsTimeDelta: longword; |
||
59 | LastTimeFrame: longword; |
||
60 | // the number of ms each frame has in its budget |
||
61 | FrameRateBudget: longword; // this is just for the graphics system |
||
62 | // see the properties section |
||
63 | fTimeDelta: longword; |
||
64 | |||
65 | TimeFrame: longword; |
||
66 | fFrameRate: longword; |
||
67 | fFrameRateGoal: longword; |
||
68 | |||
69 | fNoGraphics: boolean; |
||
70 | // sets up various timing stuff |
||
71 | procedure InitTiming(FrameRate: longword); |
||
72 | public |
||
73 | { Public declarations } |
||
74 | constructor Create(AFrameRateGoal: longword); |
||
75 | // Calculates the time delta from the last frame |
||
76 | procedure CalcNewTimeDelta; |
||
77 | // Checks if the designated time till the next render has passed |
||
78 | function TimeToRender: boolean; |
||
79 | // Prepares internal info for the next frame |
||
80 | procedure UpdateGraphicsTimeDelta; |
||
81 | procedure CalcFrameRate; |
||
82 | |||
83 | property GraphicsTimeDelta: longword read fGraphicsTimeDelta; |
||
84 | // used to determine how much time has elapsed from the last frame in ms |
||
85 | property TimeDelta: longword read fTimeDelta; |
||
86 | // The timestamp for the current frame |
||
87 | property FrameStamp: longword read TimeFrame; |
||
88 | // The frame rate that is being achieved |
||
89 | property FrameRate: longword read fFrameRate; |
||
90 | // The goal frame rate for the frame skipping mechanism |
||
91 | // This also Limits the frame rate to this to |
||
92 | property FrameRateGoal: longword read fFrameRateGoal write InitTiming; |
||
93 | |||
94 | // TimeTorender always returns false, used to disable graphics stuff |
||
95 | property NoGraphics: boolean read fNoGraphics write fNoGraphics; |
||
96 | end; |
||
97 | |||
98 | TStateMachine = class; |
||
99 | |||
100 | TState = class(TObject) // a state aware class |
||
101 | private |
||
102 | fStateIndex: integer; |
||
103 | fStateMachine: TStateMachine; |
||
104 | public |
||
105 | constructor Create; virtual; |
||
106 | |||
107 | procedure Execute; virtual; abstract; |
||
108 | property StateIndex: integer read fStateIndex; |
||
109 | property StateMachine: TStateMachine read fStateMachine; |
||
110 | end; |
||
111 | |||
112 | // This is a state machine which removes the need for large case statements when executing the current state |
||
113 | TStateMachine = class(TObject) |
||
114 | private |
||
115 | { Private declarations } |
||
116 | protected |
||
117 | { Protected declarations } |
||
118 | // Holds a list of the valid states |
||
119 | fStatesList: Tlist; |
||
120 | // the number of states |
||
121 | fStates: integer; |
||
122 | // The max number of allowed states |
||
123 | fMaxStates: integer; |
||
124 | // The current state |
||
125 | fCurrentState: TState; |
||
126 | // Function GetState(index :integer) : TState; virtual; |
||
127 | public |
||
128 | { Public declarations } |
||
129 | constructor Create; |
||
130 | destructor Destroy; override; |
||
131 | // This registers a state, if the state exists then it is updated |
||
132 | // Dont pass < 0 as the state constant |
||
133 | procedure AddState(State: TState); virtual; |
||
134 | // This removes a registerd state |
||
135 | procedure RemoveState(State: TState); virtual; |
||
136 | // This clears all the states |
||
137 | procedure ClearStates; virtual; |
||
138 | |||
139 | // allows easy state changes |
||
140 | procedure ActivateNextState; virtual; |
||
141 | // The current state's id |
||
142 | property CurrentState: TState read fCurrentState write fCurrentState; |
||
143 | |||
144 | // The Number of valid states |
||
145 | property States: integer read fStates; |
||
146 | property MaxStates: integer read fMaxStates write fMaxStates; |
||
147 | { |
||
148 | // Allows direct access to the states of an object |
||
149 | Property StateList[index :integer] : TState read GetState; |
||
150 | } |
||
151 | property StateList: Tlist read fStatesList; |
||
152 | end; |
||
153 | |||
154 | const |
||
155 | // This is the default strating number of states the TStateMachine will allocate |
||
156 | StartStates = 16; // This is also what it allocates when it tries to add a new state |
||
157 | MaxNumStates = 255; // this is the max number of states allowed. Why would you want more :) |
||
158 | |||
159 | // Scans a Component and the components owned by the Component for a Component of the class type 'Classtype' |
||
160 | function FindComponentType(Aowner: TComponent; Classtype: TComponentClass): TComponent; |
||
161 | |||
162 | implementation |
||
163 | |||
164 | uses windows, Sysutils, mmsystem; |
||
165 | |||
166 | function FindComponentType(Aowner: TComponent; Classtype: TComponentClass): TComponent; |
||
167 | var |
||
168 | index: integer; |
||
169 | begin |
||
170 | if not assigned(AOwner) then |
||
171 | begin |
||
172 | result := nil; |
||
173 | exit; |
||
174 | end; |
||
175 | if AOwner is Classtype then |
||
176 | begin |
||
177 | result := AOwner; |
||
178 | end |
||
179 | else |
||
180 | begin |
||
181 | // For some reason, when in the designer AOwner.ComponentIndex = -1! |
||
182 | // thus this search algo would failed in designer mode |
||
183 | for index := 0 to AOwner.ComponentCount - 1 do |
||
184 | begin |
||
185 | if AOwner.Components[index] is Classtype then |
||
186 | begin |
||
187 | result := AOwner.Components[index]; |
||
188 | exit; |
||
189 | end; |
||
190 | end; |
||
191 | result := nil; |
||
192 | end; |
||
193 | end; |
||
194 | |||
195 | // ----------------------------------------------------------------------------- |
||
196 | // TTiming |
||
197 | // ----------------------------------------------------------------------------- |
||
198 | |||
199 | constructor TTiming.Create(AFrameRateGoal: longword); |
||
200 | begin |
||
201 | inherited create; |
||
202 | FrameRateGoal := AFrameRateGoal; |
||
203 | InitTiming(FrameRateGoal); |
||
204 | end; {TTiming} |
||
205 | |||
206 | procedure TTiming.InitTiming(FrameRate: longword); |
||
207 | begin |
||
208 | fFrameRateGoal := FrameRate; |
||
209 | FrameRateBudget := 1000 div fFrameRateGoal; |
||
210 | fGraphicsTimeDelta := 0; |
||
211 | fTimeDelta := 0; |
||
212 | TimeFrame := timegettime; |
||
213 | LastTimeFrame := TimeFrame; |
||
214 | end; {InitTiming} |
||
215 | |||
216 | procedure TTiming.CalcNewTimeDelta; |
||
217 | var |
||
218 | NewTimeFrame: Longword; |
||
219 | begin |
||
220 | // update the time delta & time frame stamps |
||
221 | NewTimeFrame := TimeGetTime; |
||
222 | fTimeDelta := NewTimeFrame - TimeFrame; |
||
223 | TimeFrame := NewTimeFrame; |
||
224 | end; {CalcNewTimeDelta} |
||
225 | |||
226 | procedure TTiming.UpdateGraphicsTimeDelta; |
||
227 | begin |
||
228 | // get how long it took to render the frame |
||
229 | LastTimeFrame := timegettime; |
||
230 | fGraphicsTimeDelta := 0; |
||
231 | end; |
||
232 | |||
233 | procedure TTiming.CalcFrameRate; |
||
234 | begin |
||
235 | if GraphicsTimeDelta <> 0 then |
||
236 | fFrameRate := 1000 div GraphicsTimeDelta |
||
237 | else |
||
238 | fFrameRate := 0; |
||
239 | end; {CalcFrameRate} |
||
240 | |||
241 | function TTiming.TimeToRender: boolean; |
||
242 | begin |
||
243 | // Check to see if we can update the screen |
||
244 | fGraphicsTimeDelta := Timegettime - LastTimeFrame; |
||
245 | result := (GraphicsTimeDelta >= FrameRateBudget) and (not fNoGraphics); |
||
246 | end; {TimeToRender} |
||
247 | |||
248 | // ----------------------------------------------------------------------------- |
||
249 | // TMousePos |
||
250 | // ----------------------------------------------------------------------------- |
||
251 | |||
252 | constructor TMousePos.Create(Control: TControl); |
||
253 | begin |
||
254 | inherited Create; |
||
255 | if not assigned(Control) then |
||
256 | raise Exception.Create('Parent can''''t = nil'); |
||
257 | fSensitivity := 1.0; |
||
258 | fClipToControl := true; |
||
259 | SetControl(Control); |
||
260 | end; {Create} |
||
261 | |||
262 | destructor TMousePos.Destroy; |
||
263 | begin |
||
264 | SetControl(nil); |
||
265 | inherited; |
||
266 | end; {Destroy} |
||
267 | |||
268 | procedure TMousePos.SetControl(AControl: TControl); |
||
269 | begin |
||
270 | assert(self <> nil); |
||
271 | if assigned(AControl) then |
||
272 | begin |
||
273 | fControl := AControl; |
||
274 | // This gets the intitial value |
||
275 | Update; |
||
276 | // This gets the correct mouse X,Y deltas |
||
277 | Update; |
||
278 | end |
||
279 | else |
||
280 | begin |
||
281 | fControl := nil; |
||
282 | end; |
||
283 | end; {SetControl} |
||
284 | |||
285 | procedure TMousePos.SetClipState(Value: boolean); |
||
286 | begin |
||
287 | if fClipToControl <> value then |
||
288 | begin |
||
289 | fClipToControl := Value; |
||
290 | // Re-obtain the co-ords with the correct option |
||
291 | Update; |
||
292 | end; |
||
293 | end; {SetClipState} |
||
294 | |||
295 | procedure TMousePos.Update; |
||
296 | var |
||
297 | pos: TPoint; |
||
298 | begin |
||
299 | // Get the latest mouse info |
||
300 | pos := mouse.CursorPos; |
||
301 | // Convert them to relative to the Control |
||
302 | pos := fControl.ScreenToClient(pos); |
||
303 | if fClipToControl then |
||
304 | begin |
||
305 | if pos.x >= fControl.width then |
||
306 | pos.x := fControl.width - 1 |
||
307 | else if pos.x < 0 then |
||
308 | pos.x := 0; |
||
309 | if pos.Y >= fControl.Height then |
||
310 | pos.Y := fControl.Height - 1 |
||
311 | else if pos.Y < 0 then |
||
312 | pos.Y := 0; |
||
313 | end; |
||
314 | // Update the mouse delta's |
||
315 | // fmouse? = old mouse pos |
||
316 | // pos.? = new mouse pos |
||
317 | fXDelta := pos.X - fMouseX; |
||
318 | fYDelta := pos.Y - fMouseY; |
||
319 | // Scale the mouse co-ords |
||
320 | if fSensitivity - 1.0 > 0.00001 then |
||
321 | begin |
||
322 | fMouseX := fMouseX + Trunc(fXDelta * fSensitivity); |
||
323 | fMouseY := fMouseY + Trunc(fYDelta * fSensitivity); |
||
324 | end |
||
325 | else |
||
326 | begin |
||
327 | fMouseX := pos.X; |
||
328 | fMouseY := pos.Y; |
||
329 | end; |
||
330 | end; {Update} |
||
331 | |||
332 | // ----------------------------------------------------------------------------- |
||
333 | // TStateMachine |
||
334 | // ----------------------------------------------------------------------------- |
||
335 | |||
336 | constructor TStateMachine.Create; |
||
337 | begin |
||
338 | inherited; |
||
339 | fMaxStates := MaxNumStates; |
||
340 | fStatesList := Tlist.create; |
||
341 | end; {Create} |
||
342 | |||
343 | destructor TStateMachine.Destroy; |
||
344 | begin |
||
345 | ClearStates; |
||
346 | fStatesList.free; |
||
347 | inherited; |
||
348 | end; {Destroy} |
||
349 | |||
350 | procedure TStateMachine.AddState(State: TState); |
||
351 | begin |
||
352 | if (fStatesList.IndexOf(State) = -1) and |
||
353 | (fStates < MaxStates) then |
||
354 | begin |
||
355 | State.fStateIndex := fStatesList.add(State); |
||
356 | State.fStateMachine := self; |
||
357 | if fCurrentState = nil then |
||
358 | fCurrentState := state; |
||
359 | inc(fStates); |
||
360 | end; |
||
361 | end; {AddState} |
||
362 | |||
363 | procedure TStateMachine.RemoveState(State: TState); |
||
364 | var |
||
365 | index: integer; |
||
366 | begin |
||
367 | index := fStatesList.IndexOf(State); |
||
368 | if (index <> -1) then |
||
369 | begin |
||
370 | if fCurrentState = state then |
||
371 | fCurrentState := nil; |
||
372 | fStatesList.Delete(index); |
||
373 | Dec(fStates); |
||
374 | end; |
||
375 | end; {RemoveState} |
||
376 | |||
377 | procedure TStateMachine.ClearStates; |
||
378 | var |
||
379 | state: Tstate; |
||
380 | begin |
||
381 | while fStatesList.count <> 0 do |
||
382 | begin |
||
383 | state := fStatesList.Items[fStatesList.count - 1]; |
||
384 | state.free; |
||
385 | fStatesList.Delete(fStatesList.count - 1); |
||
386 | end; |
||
387 | end; {ClearStates} |
||
388 | |||
389 | procedure TStateMachine.ActivateNextState; |
||
390 | var |
||
391 | index: integer; |
||
392 | begin |
||
393 | if fStatesList.count = 0 then |
||
394 | exit; |
||
395 | if fCurrentState = nil then |
||
396 | fCurrentState := fStatesList[0]; |
||
397 | index := fCurrentState.StateIndex; |
||
398 | inc(index); |
||
399 | if index >= fStatesList.count - 1 then |
||
400 | index := 0; |
||
401 | fCurrentState := Tstate(fStatesList[index]); |
||
402 | end; {ActivateNextState} |
||
403 | |||
404 | constructor Tstate.Create; |
||
405 | begin |
||
406 | inherited Create; |
||
407 | end; {Create} |
||
408 | |||
409 | end. |