[Game] List of all AHK Games

Post gaming related scripts
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: [Game] List of all AHK Games

Post by Hellbent » 02 Dec 2021, 09:24

I was looking into how to detect if two lines cross and the test code I wrote reminded me of the old computer game Missile Command so I wrote a little mini game.
It uses line intersects to detect collisions rather than using the more traditional distance between two points method.

The game is in a very basic form.
Spoiler






After tweaking the code a bit :lol:
Spoiler

User avatar
SpeedMaster
Posts: 494
Joined: 12 Nov 2016, 16:09

Re: [Game] List of all AHK Games

Post by SpeedMaster » 02 Dec 2021, 17:10

Nice demonstration. It had been a while since I had seen a new game. 8-) I think it's normal since the most popular games have already been covered.
I tested both versions. The 1st one doesn't work (no ball is shot with the mouse) :think: . The 2nd one works very well. :thumbup:
Thanks for sharing.

This game reminds me of an old demo. "Mountain Breaker" (by Ton80)
The mountain is randomly generated :thumbup:

Press F2 to reload
Press C or Left Mouse to fire
Attachments
Mortar - Mountain breaker (Demo).zip
(919.91 KiB) Downloaded 218 times

User avatar
Gio
Posts: 1247
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: [Game] List of all AHK Games

Post by Gio » 02 Dec 2021, 18:53

Nice demo @Hellbent , thanks for sharing.

I did noticed a flaw in the games "mechanic" by accident though: due to the speed of the player guns, you actually just have to press and hold the mouse in a certain position to "beat" the game :lol:

example123.png
example123.png (18.83 KiB) Viewed 5767 times

I guess it can be corrected by making the guns a little slower to recharge? :think:

User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: [Game] List of all AHK Games

Post by Hellbent » 02 Dec 2021, 20:41

SpeedMaster wrote:
02 Dec 2021, 17:10
Nice demonstration. It had been a while since I had seen a new game. 8-) I think it's normal since the most popular games have already been covered.
I tested both versions. The 1st one doesn't work (no ball is shot with the mouse) :think: . The 2nd one works very well. :thumbup:
Thanks for sharing.

Thanks.

The first example uses a hotkey ( ctrl + LButton ) to shoot. It shoots one shot at a time and switches from left side to right side and back.

Hopefully you will get around to making a new game too :idea:

This game reminds me of an old demo. "Mountain Breaker" (by Ton80)
The mountain is randomly generated :thumbup:

Press F2 to reload
Press C or Left Mouse to fire
I checked it out but it seems to be broken. The graphics were all buggy and I could only fire at the very bottom of the screen.


Making that mini game got me in the mood to make an actual version of Missile Command.

I am only a few hours in, but it is coming along at a good pace. The main thing that is currently missing is the player based weapon system
( Flak cannons ). At the moment I just have a mock explosion at the cursor location when you click. Soon I should have it like the other game and the shots will go from the bottom of the screen up to where the cursor was.


>
This gif runs at 6 frames per second. The game runs at around 30.
Temp (1).gif
Temp (1).gif (514.57 KiB) Viewed 5748 times
20211202202746.png
20211202202746.png (60.88 KiB) Viewed 5748 times


***Requires Windows 8+ and gdip.ahk
Spoiler
@Gio
The "tweaked code" is me cheating at my own game lol. I just wanted to do the pew pew :D

User avatar
Gio
Posts: 1247
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: [Game] List of all AHK Games

Post by Gio » 03 Dec 2021, 11:57

Wow! The new game is WAY better man, congrats :clap:

Hellbent wrote:
02 Dec 2021, 20:41

Making that mini game got me in the mood to make an actual version of Missile Command.

...

This gif runs at 6 frames per second. The game runs at around 30.

This can be greatly increased.

I have recently noticed some things about game development and also some things that are related specifically to AutoHotkey game development. It may be well worth it to share them with all AHK game developers: There are some weird delays in certain syntax elements (like settimers and labels) which may greatly cap Maximum FPS if you are unaware.


1. It is good practice in general game development that drawing code should NOT be written in the same routines as physics calculations. This is to avoid having an FPS increase/decrease interferr on gameplay speed).

2. For AutoHotkey, certain syntax elements (such as labels) seem to have an internal minimum delay (unrelated to SetBatchLines) that will greatly limit your maximum FPS if you are unaware.


:arrow: I will provide some example: in the code below, i have a sample unfinished "pong-like" game of mine in which the UI is drawn inside a labels. Setbatchlines, -1 is in place and there are NO coded delays. HOWEVER, since all the loop-code is in a SetTimered label, the maximum FPS is capped to ~60ish? :crazy:

*Keyboard arrows move the player paddle:

Code: Select all

#SingleInstance, Force
#Include GDIP.ahk
SetBatchLines, -1

DllCall("Winmm\timeBeginPeriod", "UInt", 3)  ; Affects all applications, not just this script's DllCall("Sleep"...), but does not affect SetTimer.
DllCall("QueryPerformanceFrequency", "Int64*", freq)

; Start gdi+
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}
 
Gui,+E0x80000 +LastFound ; +AlwaysOnTop +OwnDialogs -Caption +ToolWindow
Gui, Show, NA, AHK Pong v0-01
hwnd := WinExist()

Width := 512, Height := 326
hbm := CreateDIBSection(Width, Height)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)

CURRENT_GOALS := 0
CURRENT_ENEMY_GOALS := 0
CURRENT_FPS := 0
CURRENT_ITERATION := 0

Ball := Object()
Ball.Speed := 2
Ball.CurrentX := 241
Ball.CurrentY := 148
Ball.GoLeft := 1
Ball.GoDown := 1

Paddle_1 := Object()
Paddle_1.CurrentX := 20
Paddle_1.CurrentY := 113
Paddle_1.Width := 20
Paddle_1.Height := 100


Paddle_2 := Object()
Paddle_2.CurrentX := 472
Paddle_2.CurrentY := 63
Paddle_2.Width := 20
Paddle_2.Height := 200
Paddle_2.GoDown := 1

SetTimer, Main_Loop, 0 ; A settimer of 0 should mean no delay at all, but the label is still capped to around 60x per second !! (This is an internal delay of this syntax element).
Return



Main_Loop:
; 1
	CURRENT_ITERATION := CURRENT_ITERATION + 1

; 4s
	If ((Ball.CurrentX >= 40) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	Else if (((Ball.CurrentY + 15 < Paddle_1.CurrentY) OR (Ball.CurrentY + 15 > Paddle_1.CurrentY + Paddle_1.Height)) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	
	
	Else if ((Ball.GoLeft = 0) AND (Ball.CurrentX <= 442))
	{
		Ball.CurrentX := Ball.CurrentX + Ball.Speed
	}
	
	
	Else If ((Ball.GoLeft = 0) AND (Ball.CurrentX > 442))
	{
		If ((Ball.CurrentY + 15 >= Paddle_2.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_2.CurrentY + Paddle_2.Height))
		{
			Ball.GoLeft := 1
		}
		else
		{
			CURRENT_GOALS := CURRENT_GOALS + 1
			Ball.CurrentX := 241
			Ball.CurrentY := 148
			Ball.GoLeft := 1
			Ball.GoDown := 1
			Paddle_1.CurrentY := 113
			
			Random, NEW_LOCATION_PADDLE_2, 1, 126
			Paddle_2.CurrentY := NEW_LOCATION_PADDLE_2
		}
	}
	
	
	
	Else If ((Ball.GoLeft = 1) AND (Ball.CurrentY + 15 >= Paddle_1.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_1.CurrentY + Paddle_1.Height))
	{
		Ball.GoLeft := 0
	}
	
	If ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY <= 326 - 30) AND (Ball.GoDown = 1))
	{
		Ball.CurrentY := Ball.CurrentY + Ball.Speed
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY > 326 - 30))
	{
		Ball.GoDown := 0
	}
	
	If  ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY >= 1) AND (Ball.GoDown = 0))
	{
		Ball.CurrentY := Ball.CurrentY - Ball.Speed
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY < 1))
	{
		Ball.GoDown := 1
	}
	
	If (Ball.CurrentX <= 1)
	{
		CURRENT_ENEMY_GOALS := CURRENT_ENEMY_GOALS + 1
		Ball.CurrentX := 241
		Ball.CurrentY := 148
		Ball.GoLeft := 1
		Ball.GoDown := 1
		Paddle_1.CurrentY := 113
	}
	
	
	CURRENT_TIME_2 := A_TickCount
	IF (((CURRENT_TIME_2 - LAST_TIME_2) >= 10000) OR (LAST_TIME_2 = ""))
	{
		LAST_TIME_2 := CURRENT_TIME_2
		Ball.Speed := Ball.Speed + 1
	}
	
	If ((Paddle_2.CurrentY <= 129 ) AND (Paddle_2.GoDown = 1))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY + 3
	}
	else if ((Paddle_2.CurrentY > 1) AND (Paddle_2.GoDown = 0))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY - 3
	}
	
	If (Paddle_2.CurrentY > 129)
	{
		Paddle_2.GoDown := 0
	}
	
	If (Paddle_2.CurrentY <= 1)
	{
		Paddle_2.GoDown := 1
	}
	
	
	
	CURRENT_TIME := A_TickCount
	IF (((CURRENT_TIME - LAST_TIME) >= 1000) OR (LAST_TIME = ""))
	{
		LAST_TIME := CURRENT_TIME
		SHOW_FPS := FPS_COUNTER
		FPS_COUNTER := 0
	}
	else
	{
		FPS_COUNTER++
	}
	
	pBrush := Gdip_BrushCreateSolid(0xff000000)
	Gdip_FillRectangle(G, pBrush, 0, 0, 512, 326)

; 2
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_1.CurrentX, Paddle_1.CurrentY, Paddle_1.Width, Paddle_1.Height)

; 3
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_2.CurrentX, Paddle_2.CurrentY, Paddle_2.Width, Paddle_2.Height)
		
	Gdip_FillEllipse(G, pBrush, Ball.CurrentX, Ball.CurrentY, 30, 30)
	
	Window_X_Pos_Frame := (A_ScreenWidth - 512) / 2
	Window_Y_Pos_Frame := (A_ScreenHeight - 326) / 2
	
	Options = x10 y10 w119 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "GOALS: " . CURRENT_GOALS . " : " .  CURRENT_ENEMY_GOALS, Options, "Arial Black")
	
	Options = x412 y10 w100 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "FPS: " . SHOW_FPS, Options, "Arial Black")
	
	
	pPen := Gdip_CreatePen("0xFF909090", 2)
	;Gdip_FillRectangle(G, pPen, 0, 0, 512, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 1, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 512, 1)
	Gdip_DrawLine(G, pPen, 511, 1, 511, 326)
	Gdip_DrawLine(G, pPen, 1, 325, 511, 325)
	
	
	UpdateLayeredWindow(hwnd, hdc, Window_X_Pos_Frame, Window_Y_Pos_Frame, Width, Height)
	
	;DllCall("Sleep", "UInt", 2)  ; Must use DllCall instead of the Sleep command. 2ms sleep for every 1FPS means maximum... 500FPS.
	
	
Return

ESC::
DllCall("Winmm\timeEndPeriod", "UInt", TimePeriod)  ; Should be called to res
ExitApp


Up::
If (Paddle_1.CurrentY >= 1)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY - 6
}
Return

Down::
If (Paddle_1.CurrentY < 226)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY + 6
}
Return


Return

:arrow: But look what happens when i put all the drawing code OUTSIDE the settimered label (in a simple loop) - FPS goes over 1000! :o

Code: Select all

#SingleInstance, Force
#Include GDIP.ahk
SetBatchLines, -1

DllCall("Winmm\timeBeginPeriod", "UInt", 3)  ; Affects all applications, not just this script's DllCall("Sleep"...), but does not affect SetTimer.
DllCall("QueryPerformanceFrequency", "Int64*", freq)

; Start gdi+
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}

Gui,+E0x80000 +LastFound ; +AlwaysOnTop +OwnDialogs -Caption +ToolWindow
Gui, Show, NA, AHK Pong v0-01
hwnd := WinExist()

Width := 512, Height := 326
hbm := CreateDIBSection(Width, Height)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)

CURRENT_GOALS := 0
CURRENT_ENEMY_GOALS := 0
CURRENT_FPS := 0
CURRENT_ITERATION := 0

Ball := Object()
Ball.Speed := 2
Ball.CurrentX := 241
Ball.CurrentY := 148
Ball.GoLeft := 1
Ball.GoDown := 1

Paddle_1 := Object()
Paddle_1.CurrentX := 20
Paddle_1.CurrentY := 113
Paddle_1.Width := 20
Paddle_1.Height := 100


Paddle_2 := Object()
Paddle_2.CurrentX := 472
Paddle_2.CurrentY := 63
Paddle_2.Width := 20
Paddle_2.Height := 200
Paddle_2.GoDown := 1

SetTimer, Main_Loop, 0 ; A settimer of 0 should mean no delay at all, but the label is still capped to around 60x per second !! (This is an internal delay of this syntax element).

Loop ; This loop HOWEVER is UNAFFECTED and may run >1000x per second no problem.
{
	
	
	CURRENT_TIME := A_TickCount
	IF (((CURRENT_TIME - LAST_TIME) >= 1000) OR (LAST_TIME = ""))
	{
		LAST_TIME := CURRENT_TIME
		SHOW_FPS := FPS_COUNTER
		FPS_COUNTER := 0
	}
	else
	{
		FPS_COUNTER++
	}
	
	pBrush := Gdip_BrushCreateSolid(0xff000000)
	Gdip_FillRectangle(G, pBrush, 0, 0, 512, 326)

; 2
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_1.CurrentX, Paddle_1.CurrentY, Paddle_1.Width, Paddle_1.Height)

; 3
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_2.CurrentX, Paddle_2.CurrentY, Paddle_2.Width, Paddle_2.Height)
		
	Gdip_FillEllipse(G, pBrush, Ball.CurrentX, Ball.CurrentY, 30, 30)
	
	Window_X_Pos_Frame := (A_ScreenWidth - 512) / 2
	Window_Y_Pos_Frame := (A_ScreenHeight - 326) / 2
	
	Options = x10 y10 w119 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "GOALS: " . CURRENT_GOALS . " : " .  CURRENT_ENEMY_GOALS, Options, "Arial Black")
	
	Options = x412 y10 w100 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "FPS: " . SHOW_FPS, Options, "Arial Black")
	
	
	pPen := Gdip_CreatePen("0xFF909090", 2)
	;Gdip_FillRectangle(G, pPen, 0, 0, 512, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 1, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 512, 1)
	Gdip_DrawLine(G, pPen, 511, 1, 511, 326)
	Gdip_DrawLine(G, pPen, 1, 325, 511, 325)
	
	
	UpdateLayeredWindow(hwnd, hdc, Window_X_Pos_Frame, Window_Y_Pos_Frame, Width, Height)
	
	;DllCall("Sleep", "UInt", 2)  ; Must use DllCall instead of the Sleep command. 2ms sleep for every 1FPS means maximum... 500FPS.
}

Return



Main_Loop:
; 1
	CURRENT_ITERATION := CURRENT_ITERATION + 1

; 4s
	If ((Ball.CurrentX >= 40) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	Else if (((Ball.CurrentY + 15 < Paddle_1.CurrentY) OR (Ball.CurrentY + 15 > Paddle_1.CurrentY + Paddle_1.Height)) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	
	
	Else if ((Ball.GoLeft = 0) AND (Ball.CurrentX <= 442))
	{
		Ball.CurrentX := Ball.CurrentX + Ball.Speed
	}
	
	
	Else If ((Ball.GoLeft = 0) AND (Ball.CurrentX > 442))
	{
		If ((Ball.CurrentY + 15 >= Paddle_2.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_2.CurrentY + Paddle_2.Height))
		{
			Ball.GoLeft := 1
		}
		else
		{
			CURRENT_GOALS := CURRENT_GOALS + 1
			Ball.CurrentX := 241
			Ball.CurrentY := 148
			Ball.GoLeft := 1
			Ball.GoDown := 1
			Paddle_1.CurrentY := 113
			
			Random, NEW_LOCATION_PADDLE_2, 1, 126
			Paddle_2.CurrentY := NEW_LOCATION_PADDLE_2
		}
	}
	
	
	
	Else If ((Ball.GoLeft = 1) AND (Ball.CurrentY + 15 >= Paddle_1.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_1.CurrentY + Paddle_1.Height))
	{
		Ball.GoLeft := 0
	}
	
	If ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY <= 326 - 30) AND (Ball.GoDown = 1))
	{
		Ball.CurrentY := Ball.CurrentY + Ball.Speed
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY > 326 - 30))
	{
		Ball.GoDown := 0
	}
	
	If  ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY >= 1) AND (Ball.GoDown = 0))
	{
		Ball.CurrentY := Ball.CurrentY - Ball.Speed
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY < 1))
	{
		Ball.GoDown := 1
	}
	
	If (Ball.CurrentX <= 1)
	{
		CURRENT_ENEMY_GOALS := CURRENT_ENEMY_GOALS + 1
		Ball.CurrentX := 241
		Ball.CurrentY := 148
		Ball.GoLeft := 1
		Ball.GoDown := 1
		Paddle_1.CurrentY := 113
	}
	
	
	CURRENT_TIME_2 := A_TickCount
	IF (((CURRENT_TIME_2 - LAST_TIME_2) >= 10000) OR (LAST_TIME_2 = ""))
	{
		LAST_TIME_2 := CURRENT_TIME_2
		Ball.Speed := Ball.Speed + 1
	}
	
	If ((Paddle_2.CurrentY <= 129 ) AND (Paddle_2.GoDown = 1))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY + 3
	}
	else if ((Paddle_2.CurrentY > 1) AND (Paddle_2.GoDown = 0))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY - 3
	}
	
	If (Paddle_2.CurrentY > 129)
	{
		Paddle_2.GoDown := 0
	}
	
	If (Paddle_2.CurrentY <= 1)
	{
		Paddle_2.GoDown := 1
	}
Return

ESC::
DllCall("Winmm\timeEndPeriod", "UInt", TimePeriod)  ; Should be called to res
ExitApp


Up::
If (Paddle_1.CurrentY >= 1)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY - 6
}
Return

Down::
If (Paddle_1.CurrentY < 226)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY + 6
}
Return


Return

:arrow: YES, i know that the animation in the second example is NOT 1000 FPS smooth, but that is because the animation drawing is currently tied to absolute calculated positions (which is NOT necessary, and therefore it IS possible to smooth out the animation between frames regardless of the physics being calculated only 60x per second). But the frames ARE being drawn 1000x. I cannot provide a code right now but i have indeed achieved the smoothed FPS result with SOME animations in Archmage Gray (by delaying animations in order to use the the last 2 physics calculations to smooth the animation between these 2 calculated positions pixel-by-pixel change on the ~30 frames being drawn between calculations). I am yet to find the time to finish this implementation in Archmage Gray but i will launch a v1.3 once i do (with buttery smooth animations).

:arrow: TLDR; many AHK games are capped to ~60FPSish, but this is NOT (as i had previously thought) because AutoHotkey is "heavy" on the machine: rather it is because of these unexpected delays (not binded to SetBatchLines) in certain syntax elements such as settimered labels. Therefore it is possible to make MUCH MORE elaborated games with AutoHotkey no problem with current computers performance).

User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: [Game] List of all AHK Games

Post by Hellbent » 04 Dec 2021, 09:23

Gio wrote:
03 Dec 2021, 11:57
Wow! The new game is WAY better man, congrats :clap:
Thank you.
Hellbent wrote:
02 Dec 2021, 20:41

Making that mini game got me in the mood to make an actual version of Missile Command.

...

This gif runs at 6 frames per second. The game runs at around 30.
This can be greatly increased.
I was just trying to say that the game runs much smoother than the choppy gif, but you do bring up a topic that I am interested in learning more of.
I have recently noticed some things about game development and also some things that are related specifically to AutoHotkey game development. It may be well worth it to share them with all AHK game developers: There are some weird delays in certain syntax elements (like settimers and labels) which may greatly cap Maximum FPS if you are unaware.
Typically my methodology is based on these factors.

One is to select a frame rate that is smooth enough to achieve persistence of motion. I find 30fps to normally meet this.

Another reason for the 30 fps has to do with ahk's time keeping. As I understand it, ahk uses granular time keeping in units of 10ms OR 15ms based on the system (or something like that). So based on that I pick a time interval where they both meet ( 3 * 10 ) = ( 2 * 15 ) = ( 30 )

Here is what the docs say.
Due to the granularity of the OS's time-keeping system, Delay is typically rounded up to the nearest multiple of 10 or 15.6 milliseconds (depending on the type of hardware and drivers installed). For example, a delay between 1 and 10 (inclusive) is equivalent to 10 or 15.6 on most Windows 2000/XP systems. To achieve a shorter delay, see Examples.

The actual delay time might wind up being longer than what was requested if the CPU is under load. This is because the OS gives each needy process a slice of CPU time (typically 20 milliseconds) before giving another timeslice to the script.

A delay of 0 yields the remainder of the script's current timeslice to any other processes that need it (as long as they are not significantly lower in priority than the script). Thus, a delay of 0 produces an actual delay between 0 and 20ms (or more), depending on the number of needy processes (if there are no needy processes, there will be no delay at all). However, a Delay of 0 should always wind up being shorter than any longer Delay would have been.
If I am not understanding the point of that please correct me.


The next reason for my methodology is in how SetTimer works.

When you set a timer it will elapse the time and then do two things, one is run the routine, and two is start a new timer.



if your routine takes less than the interval you set, it will wait the remaining time and then run it on schedule, or if the routine takes longer than the interval it will run the new routine as soon as it is finished.
20211203201045.png
20211203201045.png (27.59 KiB) Viewed 5623 times
This is great because with it you can create a routine designed to take the computers performance in mind. For example, because I am on a low end laptop I can try to create my games to hover around a 30ms cycle regardless of load.

In this following example I have it set up to run at around 30ms (roughly 30FPS ) and because the time to run the routine is always less than the 30ms allowed, the frame rate never changes.

Image

On the other hand, if the time it takes to run the routine takes longer than the scheduled time it can start to lag.
20211203203813.png
20211203203813.png (7.16 KiB) Viewed 5623 times

Ok so that is the basics of my methodolog.

There are a few issues I do see with my game code though, but the way I code something that I'm not really sure how I should approach I normally just try to solve one problem at a time and sort of use duct tape to hold things together until I can start to see the bigger picture and then redesign it based on my new understanding of what needs to happen. Normally what I would tend to do is use variables to tell me what I should or should not draw at the end of the routine.

Now to compare and contrast.

I really like the idea that you are going with, as I understand it you are going to use change in time dt in your calculations.
By that I'm referring to this.
I cannot provide a code right now but i have indeed achieved this result with SOME animations in Archmage Gray (by delaying animations to the last 2 physics calculations and then smoothing the animation between these 2 positions on the ~30 frames being drawn between calculations). I am yet to find the time to finish this implementation in Archmage Gray but i will launch a v1.3 once i do (with buttery smooth animations).
In the pong game however I don't see the use of time. What I do see though is a setup that I hadn't even thought of.
This is truly eye opening for me. The idea of using a loop to do the drawing and having it set up to be interrupted with calculations updates
is wonderful. With that setup, even when it starts to take longer to draw a frame than the time covered in that frame it will still roughly keep up with real time rather than seeming to have time slow down.
20211204090908.png
20211204090908.png (20.45 KiB) Viewed 5623 times
This wouldn't be the best suit for every case because having things seem to jump ahead or teleport when things start to run slower isn't always the right fit, but in most cases it would be fine.

Thank you very much for sharing that.

While I think that your method is going to be great for animation intense games like the one I'm working on now, I don't think it will be a good fit
for times where there isn't a lot of updating of the graphics.

On another note, although it wasn't needed in your pong example, I assume that you would need to transfer data from one variable to another for the drawing routine so that values don't change in the middle of drawing something? What method have you used to do this? Wrapping the drawing routine in a function and passing the values as args so there is no chance of a update happening while doing the data transfer perhaps?

I did notice two things about the pong game.
One is that in your drawing routine you are creating new brushes every frame without also deleting them. Either move the creation of the brushes outside the loop or add in a call to delete the brushes. While I'm sure that you are aware of the brush issue someone else that copies your code might not understand why their script is using 10 gigs of ram.

The second thing is a bit of a personal preference thing, but you should get rid of the hotkeys for movement and add a key check to your update routine.

If you have more examples of using this drawing method I really would love to see them.
Again.
Thank you very much for sharing this, it is quite awesome! :bravo: :salute:

User avatar
Gio
Posts: 1247
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: [Game] List of all AHK Games

Post by Gio » 09 Dec 2021, 21:03

Hey @Hellbent ,

Sorry for taking so long to respond.

If you have more examples of using this drawing method I really would love to see them.

I have now managed to write a sample of a "smoothed drawing method" for the Pong-like sample game. Basically the ball (the rapidly moving object) has had its animation smoothed out along 100+ FPS.

First, here is a NOT SMOOTHED VERSION that simply updates position of projectiles in the frame everytime de physics is calculated (and the phyisics is being calculated ~20x per second, because it is in a settimered label). Please try running the code and following the ball with your eyes for a few seconds:

Code: Select all

#SingleInstance, Force
#Include GDIP.ahk
SetBatchLines, -1

DllCall("Winmm\timeBeginPeriod", "UInt", 3)  ; Affects all applications, not just this script's DllCall("Sleep"...), but does not affect SetTimer.
DllCall("QueryPerformanceFrequency", "Int64*", freq)

; Start gdi+
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}

Gui,+E0x80000 +LastFound ; +AlwaysOnTop +OwnDialogs -Caption +ToolWindow
Gui, Show, NA, AHK Pong v0-01
hwnd := WinExist()

Width := 512, Height := 326
hbm := CreateDIBSection(Width, Height)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)

CURRENT_GOALS := 0
CURRENT_ENEMY_GOALS := 0
CURRENT_FPS := 0
CURRENT_ITERATION := 0

Ball := Object()
Ball.Speed := 12
Ball.CurrentX := 241
Ball.CurrentY := 148
Ball.GoLeft := 1
Ball.GoDown := 1

Paddle_1 := Object()
Paddle_1.CurrentX := 20
Paddle_1.CurrentY := 113
Paddle_1.Width := 20
Paddle_1.Height := 100


Paddle_2 := Object()
Paddle_2.CurrentX := 472
Paddle_2.CurrentY := 63
Paddle_2.Width := 20
Paddle_2.Height := 200
Paddle_2.GoDown := 1

SetTimer, Main_Loop, 40 ; 40ms timer means a maximum of 25 physic recalculations per second.

Loop ; This loop HOWEVER is UNAFFECTED and may run >1000x per second no problem.
{
	DllCall("Sleep", "UInt", 2)  ; We use this to get ~120FPS. It works this way: This dllcall can sleep for less than 10ms, unlike the sleep command. Requires timebeginperiod to be set to 3 (done in the beginning of the script). Sleeping for 6ms means maximum FPS is ~166. Actual FPS is more like ~120 (tests running on my notebook, may vary on other systems.)
	
	CURRENT_TIME := A_TickCount
	IF (((CURRENT_TIME - LAST_TIME) >= 1000) OR (LAST_TIME = ""))
	{
		LAST_TIME := CURRENT_TIME
		SHOW_FPS := FPS_COUNTER
		FPS_COUNTER := 0
	}
	else
	{
		FPS_COUNTER++
	}
	
	pBrush := Gdip_BrushCreateSolid(0xff000000)
	Gdip_FillRectangle(G, pBrush, 0, 0, 512, 326)

; 2
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_1.CurrentX, Paddle_1.CurrentY, Paddle_1.Width, Paddle_1.Height)

; 3
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_2.CurrentX, Paddle_2.CurrentY, Paddle_2.Width, Paddle_2.Height)

	Gdip_FillEllipse(G, pBrush, BALL_LAST_X, BALL_LAST_Y, 30, 30)
	
	Window_X_Pos_Frame := (A_ScreenWidth - 512) / 2
	Window_Y_Pos_Frame := (A_ScreenHeight - 326) / 2
	
	Options = x10 y10 w119 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "GOALS: " . CURRENT_GOALS . " : " .  CURRENT_ENEMY_GOALS, Options, "Arial Black")
	
	Options = x312 y10 w200 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "PHYS_CALCS_PER_SEC: " . SHOW_PHYS . "`nFPS: " . SHOW_FPS . "`nNOT SMOOTHED VERSION !!", Options, "Arial Black")
	
	
	
	pPen := Gdip_CreatePen("0xFF909090", 2)
	;Gdip_FillRectangle(G, pPen, 0, 0, 512, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 1, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 512, 1)
	Gdip_DrawLine(G, pPen, 511, 1, 511, 326)
	Gdip_DrawLine(G, pPen, 1, 325, 511, 325)
	
	
	UpdateLayeredWindow(hwnd, hdc, Window_X_Pos_Frame, Window_Y_Pos_Frame, Width, Height)
	
	;DllCall("Sleep", "UInt", 2)  ; Must use DllCall instead of the Sleep command. 2ms sleep for every 1FPS means maximum... 500FPS.
}

Return



Main_Loop:

	CURRENT_TIME := A_TickCount
	IF (((CURRENT_TIME - LAST_TIME_PHYS) >= 1000) OR (LAST_TIME_PHYS = ""))
	{
		LAST_TIME_PHYS := CURRENT_TIME
		SHOW_PHYS := PHYS_COUNTER
		PHYS_COUNTER := 0
	}
	else
	{
		PHYS_COUNTER++
	}



; 1
	CURRENT_ITERATION := CURRENT_ITERATION + 1

	BALL_LAST_X := Ball.CurrentX
	BALL_LAST_Y := Ball.CurrentY

; 4s
	If ((Ball.CurrentX >= 40) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	Else if (((Ball.CurrentY + 15 < Paddle_1.CurrentY) OR (Ball.CurrentY + 15 > Paddle_1.CurrentY + Paddle_1.Height)) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	
	
	Else if ((Ball.GoLeft = 0) AND (Ball.CurrentX <= 442))
	{
		Ball.CurrentX := Ball.CurrentX + Ball.Speed
	}
	
	
	Else If ((Ball.GoLeft = 0) AND (Ball.CurrentX > 442))
	{
		If ((Ball.CurrentY + 15 >= Paddle_2.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_2.CurrentY + Paddle_2.Height))
		{
			Ball.GoLeft := 1
		}
		else
		{
			CURRENT_GOALS := CURRENT_GOALS + 1
			Ball.CurrentX := 241
			Ball.CurrentY := 148
			Ball.GoLeft := 1
			Ball.GoDown := 1
			Paddle_1.CurrentY := 113
			
			Random, NEW_LOCATION_PADDLE_2, 1, 126
			Paddle_2.CurrentY := NEW_LOCATION_PADDLE_2
		}
	}
	
	
	
	Else If ((Ball.GoLeft = 1) AND (Ball.CurrentY + 15 >= Paddle_1.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_1.CurrentY + Paddle_1.Height))
	{
		Ball.GoLeft := 0
	}
	
	;If ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY <= 326 - 30) AND (Ball.GoDown = 1))
	If ((Ball.CurrentY <= 326 - 30) AND (Ball.GoDown = 1))
	{
		Ball.CurrentY := Ball.CurrentY + 1
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY > 326 - 30))
	{
		Ball.GoDown := 0
	}
	
	If  ((Ball.CurrentY >= 1) AND (Ball.GoDown = 0))
	{
		Ball.CurrentY := Ball.CurrentY - 1
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY < 1))
	{
		Ball.GoDown := 1
	}
	
	If (Ball.CurrentX <= 1)
	{
		CURRENT_ENEMY_GOALS := CURRENT_ENEMY_GOALS + 1
		Ball.CurrentX := 241
		Ball.CurrentY := 148
		Ball.GoLeft := 1
		Ball.GoDown := 1
		Paddle_1.CurrentY := 113
	}
	
	
	CURRENT_TIME_2 := A_TickCount
	IF (((CURRENT_TIME_2 - LAST_TIME_2) >= 10000) OR (LAST_TIME_2 = ""))
	{
		LAST_TIME_2 := CURRENT_TIME_2
		;Ball.Speed := Ball.Speed + 1
	}
	
	If ((Paddle_2.CurrentY <= 129 ) AND (Paddle_2.GoDown = 1))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY + 3
	}
	else if ((Paddle_2.CurrentY > 1) AND (Paddle_2.GoDown = 0))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY - 3
	}
	
	If (Paddle_2.CurrentY > 129)
	{
		Paddle_2.GoDown := 0
	}
	
	If (Paddle_2.CurrentY <= 1)
	{
		Paddle_2.GoDown := 1
	}
Return

ESC::
DllCall("Winmm\timeEndPeriod", "UInt", TimePeriod)  ; Should be called to res
ExitApp


Up::
If (Paddle_1.CurrentY >= 1)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY - 6
}
Return

Down::
If (Paddle_1.CurrentY < 226)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY + 6
}
Return


Return

:arrow: Now for the smoothed version: in the script bellow we are using the drawing loop (which is over 200 frames per sec since we moved the drawing code OUTSIDE the settimered label)
to update the position of the ball 1 pixel at a time. The result is this: even if every physics calculation determines the new position of the ball to be 12 pixels away from the previous one, we are still moving the drawing only 1 pixel at a time (up to these 12), which results in the same speed of the ball BUT with a MUCH more smooth movement.

As with before, please run the script below and follow the ball movement for a few secs. Notice how much more smooth the movement really is now.

Code: Select all

#SingleInstance, Force
#Include GDIP.ahk
SetBatchLines, -1

DllCall("Winmm\timeBeginPeriod", "UInt", 3)  ; Affects all applications, not just this script's DllCall("Sleep"...), but does not affect SetTimer.
DllCall("QueryPerformanceFrequency", "Int64*", freq)

; Start gdi+
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}

Gui,+E0x80000 +LastFound ; +AlwaysOnTop +OwnDialogs -Caption +ToolWindow
Gui, Show, NA, AHK Pong v0-01
hwnd := WinExist()

Width := 512, Height := 326
hbm := CreateDIBSection(Width, Height)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)

CURRENT_GOALS := 0
CURRENT_ENEMY_GOALS := 0
CURRENT_FPS := 0
CURRENT_ITERATION := 0

Ball := Object()
Ball.Speed := 12
Ball.CurrentX := 241
Ball.CurrentY := 148
Ball.GoLeft := 1
Ball.GoDown := 1

Paddle_1 := Object()
Paddle_1.CurrentX := 20
Paddle_1.CurrentY := 113
Paddle_1.Width := 20
Paddle_1.Height := 100


Paddle_2 := Object()
Paddle_2.CurrentX := 472
Paddle_2.CurrentY := 63
Paddle_2.Width := 20
Paddle_2.Height := 200
Paddle_2.GoDown := 1

SetTimer, Main_Loop, 40 ; 40ms timer means a maximum of 25 physic recalculations per second.

Loop ; This loop HOWEVER is UNAFFECTED and may run >1000x per second no problem.
{
	DllCall("Sleep", "UInt", 2)  ; We use this to get ~120FPS. It works this way: This dllcall can sleep for less than 10ms, unlike the sleep command. Requires timebeginperiod to be set to 3 (done in the beginning of the script). Sleeping for 6ms means maximum FPS is ~166. Actual FPS is more like ~120 (tests running on my notebook, may vary on other systems.)
	
	CURRENT_TIME := A_TickCount
	IF (((CURRENT_TIME - LAST_TIME) >= 1000) OR (LAST_TIME = ""))
	{
		LAST_TIME := CURRENT_TIME
		SHOW_FPS := FPS_COUNTER
		FPS_COUNTER := 0
	}
	else
	{
		FPS_COUNTER++
	}
	
	pBrush := Gdip_BrushCreateSolid(0xff000000)
	Gdip_FillRectangle(G, pBrush, 0, 0, 512, 326)

; 2
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_1.CurrentX, Paddle_1.CurrentY, Paddle_1.Width, Paddle_1.Height)

; 3
	pBrush := Gdip_BrushCreateSolid(0xff909090)
	Gdip_FillRectangle(G, pBrush, Paddle_2.CurrentX, Paddle_2.CurrentY, Paddle_2.Width, Paddle_2.Height)


	/*
		SMOOTHING CODE FOR THE BALL MOVEMENT ANIMATION
		Works like this: We took the drawing code outside the settimered label that controls physics, so we are now succesfully drawing >200FPS while physics is only being calculated ~20x per sec (therefore >10 frames are drawn between each physics recalc).
		At first, we were simply drawing the ball frame-by-frame in same exact position last calculated by the physics routine (therefore we would draw the ball ~10x in the exact same place before updating the position ~12 pixels away - because the ball object has a speed of 12).
		BUT NOW we are doing something else: We are using the second-last calculated positon AND the last calculated position to "predict" the direction that the ball is taking. With this "prediction" we can then draw the ball 1-pixel away every single frame without recalculating the entire physics. So the ball DOES move 200x per second now (as far the animation goes).
		NOTE: Depending on how we do this, we may incur in an (small) increased delay between a new calculation and the actual drawing of the ball in that position, but for this project, this is of no consequence. It is also important to note that this is not the only way to do things: there may well be better thought algoritms that nullify these side effects. 
	*/
	
	If ((BALL_LAST_Y > Ball.CurrentY) AND (Ball.GoDown = 0))
	{
		BALL_LAST_Y := BALL_LAST_Y - 1
	}
	Else if ((BALL_LAST_Y < Ball.CurrentY) AND (Ball.GoDown = 1))
	{
		BALL_LAST_Y := BALL_LAST_Y + 1
	}
	
	If ((BALL_LAST_X > Ball.CurrentX) AND (Ball.GoLeft = 1))
	{
		BALL_LAST_X := BALL_LAST_X - 1
	}
	Else if ((BALL_LAST_X < Ball.CurrentX) AND (Ball.GoLeft = 0))
	{
		BALL_LAST_X := BALL_LAST_X + 1
	}
	
	/*
	End of the code that draws the ball every FPS
	*/
	

	Gdip_FillEllipse(G, pBrush, BALL_LAST_X, BALL_LAST_Y, 30, 30)
	
	Window_X_Pos_Frame := (A_ScreenWidth - 512) / 2
	Window_Y_Pos_Frame := (A_ScreenHeight - 326) / 2
	
	Options = x10 y10 w119 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "GOALS: " . CURRENT_GOALS . " : " .  CURRENT_ENEMY_GOALS, Options, "Arial Black")
	
	Options = x312 y10 w200 cFF909090 r1 s12
	Gdip_TextToGraphics(G, "PHYS_CALCS_PER_SEC: " . SHOW_PHYS . " FPS: " . SHOW_FPS, Options, "Arial Black")
	
	
	pPen := Gdip_CreatePen("0xFF909090", 2)
	;Gdip_FillRectangle(G, pPen, 0, 0, 512, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 1, 326)
	Gdip_DrawLine(G, pPen, 1, 1, 512, 1)
	Gdip_DrawLine(G, pPen, 511, 1, 511, 326)
	Gdip_DrawLine(G, pPen, 1, 325, 511, 325)
	
	
	UpdateLayeredWindow(hwnd, hdc, Window_X_Pos_Frame, Window_Y_Pos_Frame, Width, Height)
	
	;DllCall("Sleep", "UInt", 2)  ; Must use DllCall instead of the Sleep command. 2ms sleep for every 1FPS means maximum... 500FPS.
}

Return



Main_Loop:

	CURRENT_TIME := A_TickCount
	IF (((CURRENT_TIME - LAST_TIME_PHYS) >= 1000) OR (LAST_TIME_PHYS = ""))
	{
		LAST_TIME_PHYS := CURRENT_TIME
		SHOW_PHYS := PHYS_COUNTER
		PHYS_COUNTER := 0
	}
	else
	{
		PHYS_COUNTER++
	}



; 1
	CURRENT_ITERATION := CURRENT_ITERATION + 1

	BALL_LAST_X := Ball.CurrentX
	BALL_LAST_Y := Ball.CurrentY

; 4s
	If ((Ball.CurrentX >= 40) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	Else if (((Ball.CurrentY + 15 < Paddle_1.CurrentY) OR (Ball.CurrentY + 15 > Paddle_1.CurrentY + Paddle_1.Height)) AND (Ball.GoLeft = 1))
	{
		Ball.CurrentX := Ball.CurrentX - Ball.Speed
	}
	
	
	Else if ((Ball.GoLeft = 0) AND (Ball.CurrentX <= 442))
	{
		Ball.CurrentX := Ball.CurrentX + Ball.Speed
	}
	
	
	Else If ((Ball.GoLeft = 0) AND (Ball.CurrentX > 442))
	{
		If ((Ball.CurrentY + 15 >= Paddle_2.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_2.CurrentY + Paddle_2.Height))
		{
			Ball.GoLeft := 1
		}
		else
		{
			CURRENT_GOALS := CURRENT_GOALS + 1
			Ball.CurrentX := 241
			Ball.CurrentY := 148
			Ball.GoLeft := 1
			Ball.GoDown := 1
			Paddle_1.CurrentY := 113
			
			Random, NEW_LOCATION_PADDLE_2, 1, 126
			Paddle_2.CurrentY := NEW_LOCATION_PADDLE_2
		}
	}
	
	
	
	Else If ((Ball.GoLeft = 1) AND (Ball.CurrentY + 15 >= Paddle_1.CurrentY) AND (Ball.CurrentY + 15 <= Paddle_1.CurrentY + Paddle_1.Height))
	{
		Ball.GoLeft := 0
	}
	
	;If ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY <= 326 - 30) AND (Ball.GoDown = 1))
	If ((Ball.CurrentY <= 326 - 30) AND (Ball.GoDown = 1))
	{
		Ball.CurrentY := Ball.CurrentY + 1
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY > 326 - 30))
	{
		Ball.GoDown := 0
	}
	
	If  ((Ball.CurrentY >= 1) AND (Ball.GoDown = 0))
	{
		Ball.CurrentY := Ball.CurrentY - 1
	}
	Else if ((Mod(CURRENT_ITERATION, 10) = 0) AND (Ball.CurrentY < 1))
	{
		Ball.GoDown := 1
	}
	
	If (Ball.CurrentX <= 1)
	{
		CURRENT_ENEMY_GOALS := CURRENT_ENEMY_GOALS + 1
		Ball.CurrentX := 241
		Ball.CurrentY := 148
		Ball.GoLeft := 1
		Ball.GoDown := 1
		Paddle_1.CurrentY := 113
	}
	
	
	CURRENT_TIME_2 := A_TickCount
	IF (((CURRENT_TIME_2 - LAST_TIME_2) >= 10000) OR (LAST_TIME_2 = ""))
	{
		LAST_TIME_2 := CURRENT_TIME_2
		;Ball.Speed := Ball.Speed + 1
	}
	
	If ((Paddle_2.CurrentY <= 129 ) AND (Paddle_2.GoDown = 1))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY + 3
	}
	else if ((Paddle_2.CurrentY > 1) AND (Paddle_2.GoDown = 0))
	{
		Paddle_2.CurrentY := Paddle_2.CurrentY - 3
	}
	
	If (Paddle_2.CurrentY > 129)
	{
		Paddle_2.GoDown := 0
	}
	
	If (Paddle_2.CurrentY <= 1)
	{
		Paddle_2.GoDown := 1
	}
Return

ESC::
DllCall("Winmm\timeEndPeriod", "UInt", TimePeriod)  ; Should be called to res
ExitApp


Up::
If (Paddle_1.CurrentY >= 1)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY - 6
}
Return

Down::
If (Paddle_1.CurrentY < 226)
{
	Paddle_1.CurrentY := Paddle_1.CurrentY + 6
}
Return


Return

Notice the difference? There is MUCH more that can be done once the full potential of the loop is unlocked (besides smoothing animations).

This is truly eye opening for me. The idea of using a loop to do the drawing and having it set up to be interrupted with calculations updates
is wonderful. With that setup, even when it starts to take longer to draw a frame than the time covered in that frame it will still roughly keep up with real time rather than seeming to have time slow down.

Exactly. That is one of the benefits of using multiple timers (while also avoiding the hidden delay of settimers).

I did notice two things about the pong game.
One is that in your drawing routine you are creating new brushes every frame without also deleting them. Either move the creation of the brushes outside the loop or add in a call to delete the brushes. While I'm sure that you are aware of the brush issue someone else that copies your code might not understand why their script is using 10 gigs of ram.

The second thing is a bit of a personal preference thing, but you should get rid of the hotkeys for movement and add a key check to your update routine.

Sorry about that :oops: it was just a sample code (dirty code though, i must admit).

On another note, although it wasn't needed in your pong example, I assume that you would need to transfer data from one variable to another for the drawing routine so that values don't change in the middle of drawing something? What method have you used to do this? Wrapping the drawing routine in a function and passing the values as args so there is no chance of a update happening while doing the data transfer perhaps?

As far as the sample code goes this is not a problem that has been specifically addressed. If it seems addressed, than perhaps by chance it is not a problem atm? :beer:

User avatar
SpeedMaster
Posts: 494
Joined: 12 Nov 2016, 16:09

Re: [Game] List of all AHK Games

Post by SpeedMaster » 17 Dec 2021, 13:27

New Game:

[ 05 Dec 2021] - AutoHotkey Missile Command (by Hellbent) 8-)

Thanks :thumbup:

GameNtt
Posts: 154
Joined: 19 Aug 2022, 03:36

Re: [Game] List of all AHK Games

Post by GameNtt » 19 Aug 2022, 03:55

How do you play these games tho? :?:

gregster
Posts: 8916
Joined: 30 Sep 2013, 06:48

Re: [Game] List of all AHK Games

Post by gregster » 19 Aug 2022, 03:58

GameNtt wrote:
19 Aug 2022, 03:55
How do you play these games tho? :?:
Did you install AutoHotkey? These are usually uncompiled scripts which need to be interpreted.
You might want to be more specific, if that doesn't help you.

GameNtt
Posts: 154
Joined: 19 Aug 2022, 03:36

Re: [Game] List of all AHK Games

Post by GameNtt » 19 Aug 2022, 09:14

I have AHK installed. I downloaded one of these scripts. I just don't know what to do next.


GameNtt
Posts: 154
Joined: 19 Aug 2022, 03:36

Re: [Game] List of all AHK Games

Post by GameNtt » 19 Aug 2022, 10:47

I don't see how that helps.

BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: [Game] List of all AHK Games

Post by BoBo » 19 Aug 2022, 10:49

That's a problem.

GameNtt
Posts: 154
Joined: 19 Aug 2022, 03:36

Re: [Game] List of all AHK Games

Post by GameNtt » 19 Aug 2022, 10:51

@BoBo What do you even mean? Also which section of it are you referring to? The 4th section?

Post Reply

Return to “Gaming Scripts (v1)”