Millisecs since year xxxx/1900

Monkey Forums/Monkey Beginners/Millisecs since year xxxx/1900

Arabia(Posted 2015) [#1]
I can't find a function for this, wasn't this a standard function in Unix? Do other languages also have a function for this or would you need to write one?

Just thinking about time management type games where something might take 5 hours to "grow" - using the Millisecs() function is obviously not going to work in this situation since the user might quit from the app and come back later.

It's not an overly difficult function to create, but if it is in there somewhere that I can't find, why recreate the wheel :)

If it's not currently available,, I'll write a function to do it and post it here - feel free to post your own solution though - I'm sure it can be coder a lot better than I'll do it.

On a somewhat related subject, working out if a year is a leap year was one of the very first programming exercises I was given when I first started studying programming in tertiary education nearly 25 years ago - yeah it's simple, but they started everyone from a level of knowing nothing about programming so this sort of thing was a good starting point. I think we also had to convert the year into Roman numerals - which is not as easy as you might think.


Gerry Quinn(Posted 2015) [#2]
I haven't used it, but AFAIK App.GetDate() should meet your needs.


ImmutableOctet(SKNG)(Posted 2015) [#3]
It's not necessarily what you're looking for, but my 'regal.eternity' module may be interesting to you. I haven't messed with it in a while, but it should still work. The problem with 'eternity' is that long durations are approximated. The only way to fix this is to slow it down significantly, which may be what I'll do later down the road.

It may have dependencies. If so, the rest of 'regal' is needed to use it. It's not bad as a reference, though.


skid(Posted 2015) [#4]
Monkey only guarantees 32 bit integer and float cross platform so an API should ideally use a type that keeps seconds and nanos (10-9) in two distinct fields.

Calculating elapsed times with separate fields means you need to do a special 10^9 carry check on the nano portion but otherwise it's pretty simple and it seems that nanos are the new millis in most modern OS time stamps so hooking it up to native API will in most cases be quite painless.


Arabia(Posted 2015) [#5]
@Gerry Quinn - yep GetDate is the function to use, but working out the millisecs elapsed will be left up to you as the programmer.

@skid - yeah I did notice that obviously an INT will not be adequate. Depending on the type of time management game, millsecs may not even be necessary. Farmville for example, where something takes 8 hours to grow - it would be neither here nor there tracking 8 hours down to the exact millisecond.

Other games (like Cooking Fever that I'm currently hooked) on - where stuff takes 6-10 seconds to cook, then tracking stuff to the millisec is important. The only tricky, well it's not that tricky, really is handling the time when a player puts the game into suspend or hits a pause button. It's then not a simple matter of current time - start time = total time. I know how to get around this.

Again, I'll probably go no where with this, but it is interesting to me anything to play some games and think programatically as to how they are doing what they are doing.

I'll still write the millsecs elapsed since 19xx function when I can be bother, I'm sure someone will find it useful.

EDIT: Thinking about the Int issue - if you aren't going to used the unsigned portion, then it really only leaves room for 68 years give or take for the number of seconds elapsed. For what I've described above - in the Farmville example it doesn't matter about storing time when the game is paused but in these games, things continue to happen when the game is unattended. For the Cooking Fever example, it's only necessary to track time while the game is active - possibly making what I was asking in the first place a complete moot question :)

A more useful library would be date functions where you can pass in the standard date array that Monkey uses, choose to add or subtract a period from this and then return the results in a new array as well as obviously getting time differences between two dates & times.


Arabia(Posted 2015) [#6]
@Gerry Quinn - yep GetDate is the function to use, but working out the millisecs elapsed will be left up to you as the programmer.

@skid - yeah I did notice that obviously an INT will not be adequate. Depending on the type of time management game, millsecs may not even be necessary. Farmville for example, where something takes 8 hours to grow - it would be neither here nor there tracking 8 hours down to the exact millisecond.

Other games (like Cooking Fever that I'm currently hooked) on - where stuff takes 6-10 seconds to cook, then tracking stuff to the millisec is important. The only tricky, well it's not that tricky, really is handling the time when a player puts the game into suspend or hits a pause button. It's then not a simple matter of current time - start time = total time. I know how to get around this.

Again, I'll probably go no where with this, but it is interesting to me anything to play some games and think programatically as to how they are doing what they are doing.

I'll still write the millsecs elapsed since 19xx function when I can be bother, I'm sure someone will find it useful.

EDIT: Thinking about the Int issue - if you aren't going to used the unsigned portion, then it really only leaves room for 68 years give or take for the number of seconds elapsed. For what I've described above - in the Farmville example it doesn't matter about storing time when the game is paused but in these games, things continue to happen when the game is unattended. For the Cooking Fever example, it's only necessary to track time while the game is active - possibly making what I was asking in the first place a complete moot question :)

A more useful library would be date functions where you can pass in the standard date array that Monkey uses, choose to add or subtract a period from this and then return the results in a new array as well as obviously getting time differences between two dates & times.


Arabia(Posted 2016) [#7]
Just back to the Cooking Fever example, here is a quick little program I ripped up. I'm sure there are better ways to do certain things, so please elaborate if you know of a better way of doing things - particularly the handling of whether a key is pressed or not. Currently I check if it's hit, and then wait for it to be not down before checking if it's down again if that makes sense. Otherwise holding down the key A (places a new burger on the grill) or P (Pause) will just put all the burgers down really quickly or toggle pause on/off/on/off really quickly.

Anyway...

Press A to place a new burger on the grill - it cooks in 10 seconds and burns after 15 seconds.
Press P to pause the "game" during which time nothing is cooked until the game is unpaused.

CookClass.Monkey

Import mojo

Class Burger
	Field cookingTime:Int
	Field x:Int
	Field y:Int
	Field burnsAt:Int
	Field doneAt:Int
	

	Method New(burgerNo:Int)
		Self.cookingTime = 0
		Self.burnsAt = 15000
		Self.doneAt = 10000
		Self.x = 50
		Self.y = 100 + burgerNo * 50
	End Method
	
	Method Draw()
		SetColor(0, 255, 0)
		
		If Self.cookingTime < doneAt Then SetColor(255, 255, 0)
		If Self.cookingTime > doneAt And Self.cookingTime < burnsAt Then SetColor(0, 255, 0)
		If Self.cookingTime > burnsAt Then SetColor(255, 0, 0)
		DrawRect(Self.x - 16, Self.y - 16, 32, 32)
		SetColor(255, 255, 255)
	End Method

End Class


CookingGame.Monkey

Strict

Import CookClass

Function Main:Int()
	New Game()
	Return 0
End

Class Game Extends App

	Field burgers:List<Burger> = New List<Burger>()
	Field burgerOn:Bool = False
	Field gamePaused:Bool = False
	Field keyIsDown:Bool = False
	Field lastMillisec:Int
	Field currMillisec:Int
	
	Method OnCreate:Int()	
		SetUpdateRate(60)
			
		Return 0
	End
	
	Method OnUpdate:Int()

		lastMillisec = currMillisec
		currMillisec = Millisecs()
		
		If KeyDown(KEY_P) And Not keyIsDown Then
			keyIsDown = True
			If gamePaused Then
				gamePaused = False
			Else
				gamePaused = True
			EndIf
			If gamePaused Then Print "Paused - Burgers are no longer cooking" Else Print "Resumed - Burgers are cooking again"
		EndIf
		If Not KeyDown(KEY_P) Then
			keyIsDown = False
		EndIf
		If KeyDown(KEY_A) And Not burgerOn
			burgerOn = True
			If burgers.Count() = 3 Then
				Print "Don't be greedy, only 3 burgers at once please"
			Else
				burgers.AddLast(New Burger(burgers.Count()))
			EndIf
		EndIf
		If Not KeyDown(KEY_A) Then
			burgerOn = False
		EndIf
		If Not gamePaused Then
			For Local x:Burger = EachIn burgers
				x.cookingTime = x.cookingTime + currMillisec - lastMillisec
			Next
		EndIf
		Return 0
	End
	
	Method OnRender:Int()
		Cls()
		DrawText(1000 / (currMillisec - lastMillisec) + " FPS", 10, 10)
		Local y:Int = 100
		For Local b:Burger = EachIn burgers
			DrawText("Burger cooking for " + b.cookingTime, 100, y)
			b.Draw()

			If b.cookingTime < b.burnsAt Then
				DrawText("Burger is " + b.cookingTime + " millisecs old", 300, y)
			Else
				DrawText("This burger is toast!  You left it on too long", 300, y)
			EndIf
			y = y + 50
		Next
		Return 0
	End

	Method OnLoading:Int()
		
		Return 0
	End
	
	Method OnResize:Int()
		
		Return 0
	End
	
	Method OnSuspend:Int()
	
		Return 0
	End
	
	Method OnResume:Int()
		
		Return 0
	End	

	Method OnClose:Int()
				
		Return Super.OnClose()
	End

	Method OnBack:Int()

		Return Super.OnBack()
	End
	
End


The whole point of the code is just to see work out how to handle games where things "happen" in real time but you also want the game to suspend when the user pauses the game for whatever reason.


Gerry Quinn(Posted 2016) [#8]
"The whole point of the code is just to see work out how to handle games where things "happen" in real time but you also want the game to suspend when the user pauses the game for whatever reason."

Another option is to have a 'non-paused' timer at app or window level - similar to your cookingTime but global or used my many objects within a given reference frame. Objects will refer to this instead of the real timer or Millisecs, so when you pause the game, all the animations automatically stop, and when you unpause they will continue where they left off.

A similar idea is wrapping the PlaySound function in your own function that checks the sound on/off setting, so you don't have to worry about it elsewhere. Again, your game is running in an environment sheltered from user interface details it doesn't want to know about.


Arabia(Posted 2016) [#9]
@Gerry - I think I get what you are saying. Both what I've done and what you've suggested should work equally well though (I think?). I can't see either method having a drastic effect on the speed of the app which is the most important thing - e.g. it runs at close to 60fps if you are trying to run at this speed.


Gerry Quinn(Posted 2016) [#10]
Oh absolutely, it won't significantly affect speed - it's purely a matter of convenience. And if you don't have multiple types of objects that need individual pause checks, there's not even much in the way of convenience involved either!

It's just that if you do have many types, it's easier to make time stop for your game as a whole, by not giving its objects access to the actual time.


Arabia(Posted 2016) [#11]
Yes, that does make a lot more sense doing it that way. For the type of game I'm playing, you're looking at probably 12-20 objects, so it probably does make sense to do it the way you describe.