Hello All! I'm newish to Ren'py and Python and decided to try and learn more about the language through some ambitious examples--but also because I needed an Analogue clock for my game. With some research and a lot of help from the wonderful people on these boards (big shout out to Alex, apricot orange, PyTom, and MVP xela!), I came up with an analog clock I'm really happy with and I thought I'd share it with you all. All the code is here, but I'm also going to add a bunch of explanations for those people who are new to coding. I'd also like to give a really big thanks to DragoonHP who has given me some grate bug fixes. If anyone sees any more more bugs, let me know!
Note: The version of ren'py as this was originally written was 6.15, the current version was updated when ren'py was at 6.9910.
Also note...this is the second time I'm updating this post. Last time I did, everything got eaten when I went to post. So this time I'm going to save regularly while updating this post. If you see this, I'm not finished updating the post!
Contents
1. Analogue Clock as ATL
2. Uncle Mugen's Cookbook Clock
3. Another's Screen Code Clock
4. Code for my Clock Class with detailed programming explanations
5. Using the Clock in your game (if you don't care about learning to program, just skip to here to learn how to use the clock)
So I wanted an analog clock for the game I'm making. The first thing I did was search through the forums and the main ren'py site itself. I found three different models of analog clock out there. I'm going to summarize the three different approaches first. Here we go!
1. Analogue Clock as ATL
The easiest way to make the clock would be to just add the images to the game like you would any other image and then use ATL (Animation and Transformation Language) to rotate the minute and hour hand.
The ATL page is here: http://www.renpy.org/doc/html/atl.html
But most useful for newbies and relevant to the clock challenge is this excellent tutorial on ATL by saguaro: http://lemmasoft.renai.us/forums/viewto ... 51&t=16604
This thread by saguaro has a code example to put the clock together all laid out nice and neat: http://lemmasoft.renai.us/forums/viewto ... =8&t=15636
Note saguaro's code has the hour hand finishing the full 360 degrees in 60 seconds (linear 60.0 rotate 360) and the minute hand completes the rotation in 5 seconds. To make the clock work properly the linear for the hour hand would have to be "linear 86400.0 rotate 360" (there are 86400.0 seconds in a day and it takes on day for the hour hand to travel the whole 360 degrees) and the linear for the minute hand would have to be "linear 3600.0 rotate 360" (there are 3600 seconds in an hour and it takes one hour for the minute hand to travel the whole 360 degrees).
You'd set the initial coordinates with an initial rotate command outside of the repeat block when you put the images up the first time.
Remember! 6 degrees = 1 minute.
So...I suppose I would implement saguaro's ATL code like so:
Code: Select all
image clockBase = "clockbase.png"
image clockHour = "clockhour.png"
image clockMin = "clockmin.png"
show clockbase:
xalign 0.5 yalign 0.5
show clockmin:
xalign 0.5 yalign 0.5
rotate 0 #This puts the minute hand at the starting position of 12
block: #Everything in this block repeats over and over
linear 3600.0 rotate 360
repeat
show clockhour:
xalign 0.5 yalign 0.5
rotate 90 #This puts the hour hand at the 3...making it 3 o'clock
block:
linear 86400.0 rotate 360
repeat
2. Uncle Mugen's Cookbook Clock
So the next stop on the summary journey is the analogue clock in the cookbook:http://www.renpy.org/wiki/renpy/doc/coo ... alog_Clock
This was originally by Uncle Mugen, and later updated by sundownkid, so even though the page says it is out of date, it should be up to date as of 6.18. It also includes three clock image files...which are awesome--and way better than mine...use them! Mugen's code also works well. In the old example, the clock was a function--a block of code that does an action--in this case, changes the clock manually, advancing the hands based on a "minute" variable. This piece of code was the initial inspiration for what I ended up doing...even though my code may not look like it. But what was really important to my thinking was, a) the idea of manually setting the clock using a minutes variable, and the formula where the minute hand moves minutes*6 degrees a click and the hour hand moves minutes*0.5 degrees a click. The sundownkid version doesn't use functions anymore, but screen language ATL transforms and places it in a screen. But you still can specify what time you want with the minute variable and it still doesn't run in realtime. Don't worry, I'll talk about functions later.
This thread: http://lemmasoft.renai.us/forums/viewto ... f=8&t=7257 discusses how to make Mugen's clock run automatically. Note however, the answer that is settled on uses a timer function...but it is important to note that the timer stops repeating once you move forward to the next interaction...so the automatic forward motion of the clock wouldn't last throughout the entire game.
3. Another's Screen Code Clock
There is one more interesting piece of code dealing with an analogue clock I found in the forums I want to bring up. That is here: http://lemmasoft.renai.us/forums/viewto ... 95#p159812
This is by Another and what s/he has done is create the clock as a screen. This clock runs automatically, and it is pretty straight forward if you get the saguaro and mugen examples...but it does another really cool thing that is different than the others: It makes the clock time the actual time in real life using the datetime variable! Which is pretty cool, if you ask me.
So...to summarize the three different versions of clocks discussed so far:
1. As ATL put wherever you can put images normally. This version runs automatically, but I think setting the time is a bit cumbersome (you have to work out all the degrees yourself) and I think changing the time to whatever you want after the initial setting might be a bit tricky.
2. Mugen's clock was a function and now is screen language. This runs manually and makes it really easy to set the time, change the time, and advance it when you want in a story to time with events, but it doesn't have a persistent automatic running function.
3. Another's code also uses screen language for the clock. The clock runs automatically and is synced to real life time.
Okay...so what did I decide to do and why?
4. Code for my Clock Class with detailed programming explanations
Well, first off I wanted to improve my programming. I wanted to do classes and methods in Python (I know Java). I also wanted to figure out how to do Creator Defined Displayables. This was very practical because:
1) I wanted a CDD/UDD because I wanted to be able to place my clock in a frame, and images/UDDs will do that for me. For my game specifically, I wanted to place it in a frame in a control screen.
2) For the play I'm adapting, the scene starts at 3pm and there is an important meeting that happens at 3:10pm...so time is important. But I wasn't certain if the manual method or the automatic method would work best with the story (i.e. do I want the time to advance at predetermined times in the story or do I want it to continually advance in real time). I can't really know until I finish the story and see how much time it takes to play through to the end...which means that I want the clock to have the option to both manual and automatic...and then when I saw Another's code, I wanted to have the real time option for people who aren't me as well.
3) Update: Because the clock is an object I can make it do all sorts of cool things, which I finally got around to coding. So the clock can now switch between an analogue version and a digital version. In the analogue version to can have a clock ticking sound play and have the clock chime on the hour. In both versions you can set an alarm and have it go off when the time comes. In both versions you can make the seconds visible or invisible. You can dictate the size of the clock you want...and you just add it as you would any other displayable.
4) Update: In updating the clock I added a few functions. I made it possible to subtract time from the clock as well as add to it. I added a function that I needed for my game--the ability to find out how much time has passed since a given time. I also added the ability do have the clock run either on a 12hr clock or a 24hr clock. I also created a special method that should be added to the preferences, save, and load screens if you don't want the clock sounds happening when you are in those screens. Which I didn't.
So here is the code for the clock class (which I put in a "clock.rpy" file)--Even though there are comments in the code, I'll explain in more detail afterwards.
Code: Select all
init -2:
style digi_clock:
is default
font "00 fonts/DigitalRegular.ttf"
color "#f00"
yalign 0.5
init -1 python:
from datetime import datetime #This is to get the real world datetime
img = ["00 images/VintageClockBase450x450.png", "00 images/VintageClockHour450x450.png",
"00 images/VintageClockMinute450x450.png", "00 images/VintageClockSecond400x400.png",
"00 images/DigitalClockBase460x200.png"]
"""
Use your own clock images here, or use the ones I provided.
Note: your analogue clock images need to be square, your digital base can be
any size but keeping it close to the ratio of 460x200 gives the best results.
Important: You should have four images listed in this order--The Analogue
Clock Base, The Hour Hand, The Minute Hand, The Second Hand, The Digital Clock Base
"""
renpy.music.register_channel("TockBG", mixer= "sfx", loop=True)
renpy.music.register_channel("ChimeBG", mixer= "sfx", loop=False, tight=True)
renpy.music.register_channel("Alarm", mixer= "sfx", loop=False, tight=True)
"""These are here so that the various clock sounds play on their own sound channels."""
def clock_audio_pause(): #I use this to pause the audio of the clock when I go to various screens, this is also why it isn't part of the clock, but this in the demo?
try:
myClock.a_sound_on(False, False)
except:
pass
"""As a Creater Defined Displayable this needs to extend the Displayable class)"""
class Clock(renpy.Displayable):
#The clock class constructor
def __init__(self, ana=True, h=0, m=0, resize=150, sH=True, mil_time=False, **kwargs):
"""
This constructor takes the following arguments:
ana = True gives the analogue version of the clock, False gives digital
h=Hours the clock should start on
m=Minutes the clock should start on
resize=How big you want your clock images to be
sH=If you want the second hand to be visible or not
If you do not give any arguments when you create the clock, it will default to
a clock with 0 hours, 0 minutes, 150 pixels square, with a second hand showing
"""
super(Clock, self).__init__(**kwargs)
#These lines are for setting up the images used and the size of them
self.width = resize
self.d_height = (resize*32)/100
self.base_image = im.Scale(img[0], resize, resize)
self.hour_hand_image = im.Scale(img[1], resize, resize)
self.minute_hand_image = im.Scale(img[2], resize, resize)
self.second_hand_image = im.Scale(img[3], resize, resize)
self.digital_base_image = im.Scale("00 images/DigitalClockBase460x200.png",
self.width, self.d_height)
self.offset = (resize*2**0.5-resize)/2
self.second_hand_visible = sH
#Variables for handling time
self.minutes = (h*60)+m
self.seconds = self.minutes*60
self.seconds_target = 0
self.step = 0
self.old_second = None #Used in autorun
self.alarm_hr = 0
self.alarm_min = 0
#Variables for determining the clock modes
self.analogue = ana
self.auto_run = False
self.realtime_run = False
self.forward = True
self.mil_time = mil_time
#Variables for handling sound
self.second_sound_on = False
self.chime_on = False
self.last_minute = 0 #Used for chimes
self.play_chime = False #Chimes are currenlty running or not
self.alarm_on = False
self.last_aminute = 0 #Used for alarm
self.play_alarm = False #Used for alarm
# Function that continuously updates the graphics of the clock
def render(self, width, height, st, at):
if self.seconds_target:
if self.forward:
#Advances the seconds variable until it is the same as the seconds_target variable
if self.seconds_target > self.seconds:
self.seconds += self.step
#Resets the seconds_target variable
if self.seconds_target <= self.seconds:
self.seconds_target = 0
else:
if self.seconds_target < self.seconds:
self.seconds -= self.step
if self.seconds_target >= self.seconds:
self.seconds_target = 0
self.forward = True
#Makes sure the minutes variable is always in sync with the seconds variable
if self.minutes != self.seconds//60:
self.minutes = self.seconds//60
# Calculating how many seconds have passed today.
if self.seconds >= 86400:
self.seconds -= 86400
if self.seconds_target:
self.seconds_target -= 86400
elif self.seconds < 0:
self.seconds += 86400
if self.seconds_target:
self.seconds_target += 86400
#This is all the render information for the Analogue Clock
if self.analogue:
# Create transform to rotate the second hand
tM = Transform(child=self.minute_hand_image, rotate=self.seconds*0.1,
subpixel=True)
tH = Transform(child=self.hour_hand_image, rotate=self.seconds*0.008333,
subpixel=True)
# Create a render for the children.
base_render = renpy.render(self.base_image, width, height, st, at)
minute_render = renpy.render(tM, width, height, st, at)
hour_render = renpy.render(tH, width, height, st, at)
#If we are in chime_run mode, checks to see the chimes need to be rung
if self.chime_on == True:
if self.last_minute != self.minutes:
self.play_chime = False
self.last_minute = self.minutes
self.start_chime()
# Create the render we will return.
render = renpy.Render(self.width, self.width)
# Blit (draw) the child's render to our render.
render.blit(base_render, (0, 0))
render.blit(minute_render, (-self.offset, -self.offset))
render.blit(hour_render, (-self.offset, -self.offset))
#If the second hand is visible: renders, transforms, and adds it to our clock
if self.second_hand_visible:
tS = Transform(child=self.second_hand_image, rotate=self.seconds*6,
subpixel=True)
sec_render = renpy.render(tS, width, height, st, at)
render.blit(sec_render, (-self.offset, -self.offset))
#This is all the render information for the Digital Clock
else:
# create the text that will go in the Digital Clock and boxes text sits in
ftht = self.d_height * 0.8
col =Text(":", style="digi_clock", size=ftht)
time = list(Text("{0:02d}".format(item), style="digi_clock", size=ftht)
for item in self.get_time())
fxsize = (self.width-10)//4
# Determine what to display based on if the seconds are visible
if self.second_hand_visible:
digi_text = HBox(Fixed(time[0], xsize=fxsize), col, Fixed(time[1],
xsize=fxsize), col, Fixed(time[2], xsize=fxsize), xalign=0.5)
else:
digi_text = HBox(Fixed(time[0], xsize=fxsize), col, Fixed(time[1],
xsize=fxsize), xalign=0.5)
#Put all of our pieces into one Fixed Box
digi = Fixed(self.digital_base_image, digi_text, xysize=(self.width,
self.d_height))
#Create a render for our Fixed Box
digi_render = renpy.render(digi, width, height, st, at)
# Create the render we will return.
render = renpy.Render(self.width, self.d_height)
# Blit (draw) the child's render to our render.
render.blit(digi_render, (0,0))
#Runs the realclock and autoclock functions
if not self.seconds_target:
self.realclock()
self.autoclock(st)
#If we are in alarm_on mode, checks to see the alarm need to be rung
if self.alarm_on == True:
if self.last_aminute != self.minutes:
self.play_alarm = False
self.last_aminute = self.minutes
self.start_alarm()
#This makes sure our object redraws itself after it makes changes
renpy.redraw(self, 0)
# Return the render.
return render
#####These are functions you can call to do things when using the clock
#Returns the current hours, minutes, and seconds of the clock
def get_time(self):
h, m = divmod(self.minutes, 60)
h = int(h)
m = int(m)
s = self.seconds%60
s = int(s) #Can this be placed above?
if self.mil_time:
if h > 23:
h = h%24
else:
if h is 0:
h = 12
elif h > 12:
h = h%12
return h, m, s
#Directly set the time of the clock
def set_time(self, h=0, m=0):
"""
h = hours
m = minutes
"""
fh, fm = self.fix_time(h,m)
self.seconds = ((fh*60)+fm)*60
#Manually add time to the clock, with or without animation
def add_time(self, h=0, m=0, animate=0):
"""
h = hours
m = minutes
animate = the number of seconds it takes for the time to be added
"""
if self.realtime_run == False:
num = ((h*60)+m)*60
if animate:
self.step = num // float(animate*60)
self.seconds_target = self.seconds + num
else:
self.seconds += num
#Manually add time to the clock, with or without animation
def sub_time(self, h=0, m=0, animate=0):
"""
h = hours
m = minutes
animate = the number of seconds it takes for the time to be subtracted
"""
if self.realtime_run == False:
self.forward = False
num = ((h*60)+m)*60
if animate:
self.step = num // float(animate*60)
self.seconds_target = self.seconds - num
else:
self.seconds -= num
#Determines how much time has passed since a given time
def time_passed(self, start_h=0, start_m=0, days_ago=0, comb_min=True):
"""
h, m are the hours and minutes that make up the starting time you
want to test against
start_day is the day you'd like to start the time passed count from. That first day is 0
comb_min, if true it returns just the total minutes, if False returns hours and mintues
"""
oldnum = (start_h*60)+start_m
nh, nm, ns = self.get_time()
newnum = (nh*60)+nm
minpassed = newnum-oldnum
if days_ago > 0:
minpassed += (days_ago*1440)
tph, tpm = divmod(minpassed, 60)
if comb_min:
return minpassed
else:
return tph, tpm
#Sets the runmode of the clock, the second hand, and the sound
def runmode(self, mode, secOn=True, chOn=True):
"""
mode= The mode of the clock: auto, real, or none (None turns off the clock)
snd= Turns on or off sound
"""
if self.analogue:
self.a_sound_on(secOn, chOn)
if mode == "none":
self.seconds_target = 0
self.realtime_run = False
self.auto_run = False
else:
if mode == "auto":
self.realtime_run = False
self.auto_run = True
elif mode == "real":
self.auto_run = False
self.realtime_run = True
#Sets the analogue sounds for the clock
def a_sound_on(self, secOn, chOn):
"""
secOn = Turns the second hand sound on or off
chOn = Turns the chime sound on or off
"""
if self.analogue:
self.second_sound_on = secOn
self.chime_on = chOn
if self.second_sound_on:
if renpy.music.is_playing(channel='TockBG'):
renpy.music.stop(channel='TockBG', fadeout=0.1)
renpy.music.play("00 sounds/ClockTick1.flac", channel='TockBG')
else:
renpy.music.stop(channel='TockBG')
if not self.chime_on:
renpy.music.stop(channel='ChimeBG')
#Sets the alarm for the digital clock
def set_alarm (self, h=0, m=0, on=True):
fh,fm= self.fix_time(h,m)
self.alarm_hr = fh
self.alarm_min = fm
self.alarm_on = on
#####These functions are used internally, no need to call them yourself
#Returns a list of all the child displayables for this displayable.
def visit(self):
return [self.base_image, self.hour_hand_image, self.minute_hand_image,
self.second_hand_image, self.digital_base_image]
#Runs the clock mechanism automatically
def autoclock(self, st):
if self.auto_run:
self.realtime_run = False
dt = int(st)
if self.old_second != dt:
self.seconds += 1
self.old_second = dt
#Runs the clock based on the real world time
def realclock(self):
if self.realtime_run:
self.auto_run = False
t = datetime.today()
self.seconds = (3600 * t.hour) + (60 * t.minute) + t.second
#Determines how many chimes should ring at any given hour and creates audio queue
def chime_looper(self):
h, m, s = self.get_time()
if h is 0:
h = 12
elif h > 12:
h = h%12
ch = ["00 sounds/ChimePart.ogg",]*(h-1)
ch.append("00 sounds/Chime1.ogg")
return ch
#Plays clock chimes on the hour
def start_chime(self):
if self.chime_on:
if not self.play_chime:
if self.minutes%60 == 0:
self.play_chime = True
cf = self.chime_looper()
renpy.music.queue(cf, channel='ChimeBG')
#Plays the alarm when the time has arrived
def start_alarm(self):
if self.alarm_on:
if not self.play_alarm:
h, m, s = self.get_time()
if self.alarm_hr == h and self.alarm_min == m:
self.play_alarm = True
if self.analogue:
renpy.music.play("00 sounds/BellAlarm.wav", channel='Alarm')
else:
renpy.music.play("00 sounds/digital-alarm.wav", channel='Alarm')
#Make sure the time is within the 0-24hr range
def fix_time(self, h,m):
if self.mil_time:
if m > 59:
hadd, m = m%60
h += hadd
if h > 23:
h = h%24
else:
if m > 59:
hadd, m = m%60
h += hadd
if h is 0:
h = 12
elif h > 12:
h = h%12
return h,m
1) The first this we have is the creation of the style for the digital clock font. This section notes the font used, the color (red), and that is should be center aligned. Since this style is used by the clock, which is initialized at -1, this style should be initialized at -2 so it happens before the clock is made.
2) Next part of the set-up before we get to the clock itself is importing datetime. This is a Python module that allows us to access the real world time.
3) After that I place the images for the clock in a list...I'll provide the images in the file I'm uploading below (the minute hand is a bit off-center...but we'll just call it charming--again, I think you should use Uncle Mugen's analogue clock images). Your analogue clock images should be square, mine are 450x450. The last image in the list is the base image for the digital clock, that image is 460x200...and the program will stretch the digital base image to be the right size, but the closer your own image (if you use your own) is in ratio to mine, the less distortion there will be.
4) Next up I declare the channels that the clock sounds will play on. I declare one for the ticktock sound, one for the Chimes that happen on the hour, and one for the Alarm. Info on declaring audio channels can be found here: http://www.renpy.org/doc/html/audio.html
5) The last thing that happens before we get to the clock itself, is I created a function called clock_audio_pause. What this does is it tries to turn the sound of myClock off. If this isn't possible for some reason, it does nothing. What is this function for? I noticed in my game, that when I went to the save, load, or preferences screen after starting my game, the second hand ticking sound would keep going...and I didn't like that. So I created this method to turn the sound off when the player is in one of those three screens. How do I do that? With this line of code:
Code: Select all
on "show" action Function(clock_audio_pause)
Code: Select all
screen save:
# This ensures that any other menu screen is replaced.
tag menu
use navigation
use file_picker
on "show" action Function(clock_audio_pause)
Clock Class
After all of that, I define the Clock class. A class is an object...really just a collection of variables and methods all put in one convenient package. For a regular class, the only method you must have is a constructor...which explains how to make your class...this is usually where you put your variables. For a User Defined Displayable, you must have the constructor method, a render method (which continuously updates the graphics of your displayable), and a visit method (which returns a list of all the displayables in your class. But the clock wouldn't be nearly as fun if that was all we had, so I've added a lot more method.
Note: My understanding is that, a function and a method are basically the same thing, except that technically, if it belongs to a class you should call it a method, and if it doesn't you should call it a function. So clock_audio_pause() is a function because it doesn't belong to any class, and add_time() is a method because it belongs to the clock itself.
Another Note:
Info on User Defined Displayables can be found here:
http://www.renpy.org/doc/html/udd.html
http://www.renpy.org/wiki/renpy/doc/ref ... splayables
[*]Clock Class Constructor
1) First thing you do is announce that you are making a Clock class, because the Clock is a User Defined Displayable, it must extend the Displayable class. That is the first line of code:
Code: Select all
class Clock(renpy.Displayable):
2) After that in the constructor, you have to call the original (i.e. Displayable class) constructor that the Clock descents from (the "super" line). You just have to do it.
3) Finally, we have all the variables the Clock needs in order to work. The variables are preceded by "self"...because these variables belong to the clock. The first block of variables are all the ones dealing with the images and sizing them. The width of the clock is what you entered for resize (or the default). The d_height will only be used for digital clocks to figure out the perfect height. Through trial and error (and measuring) I determined that for the font I'm using for the digital clock, the height should be 32% of the width. Then I declare all the images taking them from our image list from the set up. And I have to have an offset variable. PyTom explained that when rotating images, Renpy puts that image in a larger box that as a hight and width that is the hypotenuse of the right triangles that make up that square. This means that the minute and hour hands are going to be in the wrong place once they start rotating. So, I worked out how out of place the hands will be and how much the hands would need to be offset to put them back to where they need to be based on how big of a clock you have. That is what the offset is used for. You finish up the section noting if the second hand is visible or not.
4) The next set of variables are to handle the calculation of time. Although when using the clock you enter in hours and minutes, the clock itself runs on seconds when it is in being animated. This allows for smoother animation. So the clock needs to know how many minutes it has and how many seconds it has. Those are the two main variables it uses to keep time. The minutes to deal with the user, the seconds to deal with animation. The other variables in that block are used for record keeping. The is a "second_target" and "step" variables are used to animate adding time to the clock. The "old_second" is used to work out the autorun function. And the "alarm_hr" and "alarm_min" are the variables that hold what the alarm is.
5) The next block of variables just keep track of what mode the clock is in: Analogue or Digital? Auto Run? Realtime Run? Should the clock be moving forward or backward? Should it be in military time or the regular 12hr clock?
6) After that we get the block of variables are for dealing with sound. Should the second hand ticking sound be on? Should the chimes be on? Should the alarm be on?
All of that completes the clock constructor.
[*]Clock Render Method
1) So...when creating a UDD, you must override (i.e make new, specific to your UDD versions) a few methods: The ones to think about are: the constructor method (you need to override so that you have your own thing), the render method (which is what draws the object), the visit method (if your object has children, you much provide this method that returns those children)...and while I suppose you don't have to some some sort of event method, since part of the point of having a UDD is to have it do cool things, you'll want to have some sort of even methods to do those cool things...my events involve turning the clock on and off and switching modes, setting an alarm, doing audio, etc.
2) The render method is the big deal. It is the method that makes your displayable show up.
The basic render method has a few mandatory parts: Any transforms you want to do to the images (this is optional), The render for each sub-object (child) that is a part of the whole displayable (like the second hand is a child of the clock), the main render, then you must add (blit) the children's renders to the main render, tell the method to redraw it self regularly, then return the render are you are done.
But here is the cool thing! The render method is a loop that is constantly refreshing itself. Which means you can put some other things in that method you want continuously checked...like I want the clock to continuously check if it time to ring the alarm, or continuously check if it should go into autorun, etc.
So this render starts with some stuff to make animations work. If the target time is higher than the current time, then it increases the seconds bit by bit until the time goal and the actual goal are the same. Once that happens it resets the target. Then I just want to make sure that the seconds and minute variable never are out of sync.
The next little block is there to make sure that the number of seconds never get out of hand. there are 86400 seconds in a day. If the clock goes on for multiple days, then the number of seconds get really big and there is no need to make the computer work that hard. So, whenever we reach 86400 seconds, I reset back to zero. similarly, if the seconds ever go below 0 (because you are subtracting time) then it adds 86400 so that we are never dealing with negative numbers.
All of our housekeeping out of the way, we get into the big time rendering. What the clock will look like will depend on if the clock is analogue or digital so we move to an if statement. If the clock is analogue, we render the analogue images, if it is digital, we render the digital images.
For the Analogue part, I created a transform that rotates the minute hand .1 degrees for every second in the second variable. The other transform rotates the hour hand .0083 degrees for every second in the second variable. Then you need to make a render for each child that is going to be drawn on the screen. The base screen has no transform, so the first parameter is just the base image, the other two children have transforms on them, so the first parameter is the transform I want them to have. The rest of the parameters are fixed and you have to have them just like they are.
I have a quick block that checks every time there is a new minute if the chimes are on. If there is a new minute, then then render goes and check if it should chime (start_chime() method). After that I make the render, and then blit the children's renders to the clock's renders. I finish up that analogue block with render information for the second hand, which I've put in an if block. So it will only showw up if the second_hand_visible variable is true. Which means you can turn your second hand on or off at will.
That finishes the Analogue Render section and moves into the Digital render section. I get the current time on the clock and create a text version of those numbers (with our without the seconds depending on if they are to be visible). I put each element of the time (hours, minutes, seconds) into its own Fixed box so that the numbers stay centered, and put into an HBox with all the elements evenly spaced out. Then I make a larger Fixed box that holds the digital base and that time text. I put it in a render, make an overall render, and blit it to that render. Done with the Digital specific render section!
Then I do some quick checks that are relevant to the clock regardless of digital/analogue. I check if the clock is in realclock mode or autoclock mode (over and over because the render is a loop...which means you can change the mode and the render will pick up the changes), and check if we hit the alarm.
You need to actually have the clock redraw itself regularly. So this clock redraws itself continuously. and then I return the render...so that the whole drawing process can actually happen.
[*]The methods you will want to mess with
1) get_time returns the time as three numbers: hours, minutes, seconds. It makes sure the numbers it returns are ints. I use the % function to get the remainder to good effect. Note, I also make sure that 00 hours = 12 hours when you aren't using military time, because you can't ring only 0 bells and this isn't a 24 clock.
2) set_time lets you set the time to a specific time by adding hours and minutes. I use a method I made called fix_time, to make sure if that strange numbers entered are translated into normal numbers.
3) add_time lets you add an amount of time to the time you currently have, with our without an animation. If there is an animation, it is the number of seconds you want the clock to take to get to the new time.
4) sub_time lets you subtract an amount of time to the time you currently have, with our without an animation. If there is an animation, it is the number of seconds you want the clock to take to get to the new time, running the clock backward.
5) time_passed is a method where you provide the starting time and it tells you how long has passed since that starting time. It also tracks how many days ago you want to test from. Lastly, you can say if you want the method to return two numbers (hours and minutes) or return one number, just the minutes. If it has been 1hr and 15 minutes since the start time, comb_min False would return 1, 15. comb_min True would return 75.
6) runmode I put in there to make it easier for the user. You just type in the mode you want: auto or real or non, and if you want the second hand sound on and if you want the chimes sounds on. Then method adjusts all the relevant variables for you.
7) a_sound_on lets you decide if you want the second hand sound on or the chimes on, both or neither. This method is here for it you want some fine control of the sounds, so if you want to have one on but not the other, or if you want to adjust one sound specifically.
8 ) set_alarm. Pretty self-explanatory. Sets the alarm and turns the alarm sound on or off.
[*]Methods that are used by other methods, that you probably don't need to worry about and probably won't use.
1) The last mandatory thing I have to have here is the visit method...I never use it...but I have to have it in there. I just returns the four children of the clock class. Don't ask me why. You just have to.
2) autoclock basically adds a second to the second variable every second. How do you know when a second is passed? That is the st variable. Displayable objects have an st variable that measures how long that object has been visible on screen in seconds. So I just use that st variable (that comes from the render), to add a second. Now, there is a trick. The st is all of the seconds the object has been visible and you don't want to add a gazillion seconds, you only want to add one. So I every second I subtract the old second total from the current second total, which gives me 1 (which I put in the dt variable) and I add that do seconds.
3) realclock gets the real world time in hours, minutes, and seconds, turns the whole thing into sends and continuously syncs the clocks seconds with the real world seconds.
4)chime_looper. So this method creates a queue of chime sounds. I have two different chime sounds, the first, "ChimePart" gets cut off and the final one "Chime1" fades out. So, if it is 4 o'clock, I want 3 partial chimes and one final chime. This loop gets the hour variable and makes a the list of sounds and puts in in the cf variable then returns it to whoever called it. Who called it...
5) start_chime. If the clock hits the hour and it isn't already playing chimes, it takes the cf queue of chimes and plays them.
6) start_alarm, which is just like the start_chime, but no looper. Instead, it checks to see which mode we are in. If it is a digital clock it plays a digital alarm sound, if it is analogue it plays a bell alarm.
7) The last method is the fix_time method that translates whatever strange number was given by the user into a more workable hour and minute set of numbers based on if it is a 12hr clock or a 24hr clock.
So...that is the clock class. If you have any questions about how I made it, feel free to ask and I'll answer!
But how do you use it?
5. Using the Clock in your game (if you don't care about learning to program, just skip to here to learn how to use the clock)
So what do you do if you want to use the clock?
If you just want to try out my file, download it, unzip it, and run it. If you want to put it into your own projects:
1) Download the attached Zip file and unzip it.
2) Put the clock.rpy file and the 00 images, 00 fonts, and 00 sounds folders into your projects game file.
3) If you have the chimes and the second hand on for your game, but you don't want them to sound while your are in the save, load, or preferences screen, add this line to the end of each of those screens:
Code: Select all
on "show" action Function(clock_audio_pause)
But first here is a summary of all the commands you'll generally need:
Code: Select all
$ clock_audio_pause() #This pauses the all audio of the clock and should be added on sho to thee load, save, and preferences screens
$ myClock = Clock(True, 3,0, 150, True,True)#Create a clock. Args: Analogue?(If Flase it is digital), Hours, Minutes, Size of the image, if you want a second hand?, if you want military time?
$ h, m, s = myClock.get_time() #Returns the current time's hours, minutes, and seconds
$ myClock.reset() #Sets the seconds and days to 0
$ myClock.add_time(0, 15) #Add 15 minutes to the time. Args: Hours, Minutes
$ myClock.add_time(1,30, 3) #Add 1 hour, 15 minutes, but animate the adding and have it take 3 seconds. Args: Hours, Minues, Seconds it takes for the animation
$ myClock.sub_time(0, 15) #Subtrack 15 minutes to the time. Args: Hours, Minutes
$ myClock.sub_time(1,30, 3) #Subtract 1 hour, 15 minutes, but animate the adding and have it take 3 seconds. Args: Hours, Minues, Seconds it takes for the animation
$ h,m = myClock.time_passed(2, 15, 1, False)#Returns how many hours and minutes have passed since a given starting time. Args: starting hour, starting minutes, how many days in the past, False for hours and minutes (True--the default--will return minutes only)
$ m = myClock.time_passed(2, 15) #Returns how many minutes have passed since a given starting time. Args: starting hour, starting minutes
$ myClock.runmode('auto', False, False) #Sets the runtime mode of the clock. Args: Mode ('auto', 'real', or 'none'), Then second hand and chimes sounds (True/False, True is the default, sounds won't play for digital clocks)
$ myClock.a_sound_on(False, False) #Turn the clock's second hand and/or chimes sound on/off. Args: (Seconds True/False, Chimes True/False)
$ myClock.set_alarm(5,30) #Set the alarm of your clock for 5:30 (Args: hours, minutes)
$ myClock.set_alarm(0,0,False) #Turn your alarm off
Code: Select all
define n = Character(None, color="#c8ffc8")
default myClock = Clock(True, 3, 0, 150, False, False) #Analogue or Digital, hours, minutes, size, second hand, military time
screen clock_screen:
add myClock:
xalign 0.5
yalign 0.5
# The game starts here.
label start:
show black
"Welcome to Trooper6's UDD Clock Extravaganza!"
show screen clock_screen
with Dissolve(1)
###Analogue Clock####
"So, here is the analogue clock. We are starting with the second hand not showing, the sound off, and military time is off.
It is currently 3pm."
###Setting Time, Adding Time
$ myClock.a_sound_on(False, True) #second hand sounds, chimes sounds
$ myClock.set_time(0,45) #hours, minutes
"But 3pm is too late, I think. Let's set the time to 12:45! I'm also turning on the chimes sound, however,
no chimes should ring because we jumped to the time rather than passed over an hour through animation."
$ myClock.add_time(0,15,1) #hours, minutes, how long to animate in seconds
"Fifteen minutes pass...with animation, it should take a second and we should hear chimes because we passed an hour."
$ myClock.a_sound_on(False, False) #second hand sound, chimes sound
$ myClock.add_time(1,5,5)
"Let's forward an hour and five minutes...but let's also turn the chimes sound off, so there should be no chimes.
It should take 5 seconds."
"So that is showing how you can turn on the sound of the chimes on and off, and well as how to add time in
two different ways: by jumping to a time, and by advancing to that time with animation."
###Time Passed
"You can check to see how much time has passed with the time_passed function. You have to give it a starting time though."
$ h, m = myClock.time_passed(0, 30, 0, False) #Starting hours, starting minutes, how may days ago, combined minutes?
$ n("So how much time has passed since 12:30? {0} {hour} and {1} minutes.".format(h, m, hour="hours" if h>1 else "hour")) #This is written this way so I can use a conditional on whether to use the word 'hour' or 'hours'
$ tp = myClock.time_passed(5,0,1)
"So how many minutes have passed since yesterday 5am? [tp] minutes."
###Subtracting Time
$ myClock.a_sound_on(False, True)
"Let's test the subtracting time function, with the chimes on. Let's turn back time 30 minutes.
Maybe you want time travel shenanigans!"
$myClock.sub_time(0,30) #hours, minutes
"You should see time jumped backwards"
$myClock.sub_time(1, 5, 3) #hours, minutes, how long to animage in seconds
"Let's try it with subtracting time animation style. The chimes should ring."
###Getting Time
"Now, how do you know what time it is? Just ask!"
$h,m,s = myClock.get_time() #This will return three numbersof the current time: hours, minutes, seconds
"It is [h]:[m]:[s]"
$ myClock.mil_time = True #Here I'm setting the military time to true
$myClock.set_time(13,15)
"Just showing that this can be done with military time as well."
###Fun with Alarms
$ myClock.set_alarm(13,30) #The alarm time
"Let's set an alarm on the clock, since we are still in military time, let's...say 13:30."
$ myClock.add_time(0,40,2)
"Now let's forward through time to trigger the alarm."
###Auto Mode
$ myClock.mil_time =False
$ myClock.set_time(1,59)
$ myClock.second_hand_visible = True
$ myClock.runmode("auto") # mode, you can also add True/False for second hand audio and True/False for chimes audio
"Okay, that was fun. Let's look at the two other different run modes, 'auto' and 'real,'
starting with auto. First let's turn off the military time and the alarm, then let's set
the time to 1:59 and then make the second hand visible. Then let's start the clock on
auto run. By default the second hand and chime sounds are turned on if the clock is analogue.
If you wait a short bit, you should hear chimes when the clock turns 2:00."
$ myClock.add_time(0,10,2)
"Ten minutes pass...showing you can add time even when in the auto mode."
$ myClock.sub_time(0,10,2)
"And now I'm subtracting time in auto mode."
###Real Mode
"So...are you ready to look at the next mode?"
$ myClock.second_hand_visible = False
$ myClock.runmode("real")
"So I'm switching from the autoclock mode to the realtime clock mode. But let's also take away the second
hand image while keeping the second hand sound going."
$ myClock.second_hand_visible = True
"So the real time clock is running now, but now with more second hand action!"
$ myClock.add_time(0,10)
$ myClock.add_time(0,10,3)
"Ten minutes should not pass here...I tried to add some time, but that won't work in the real time mode."
$ myClock.sub_time(0,10)
$ myClock.sub_time(0,10,3)
"Ten minutes should not be subtracted here...I tried to subtract some time, but that won't work in the real time mode."
$ myClock.runmode("none")
"Okay...I just turned off the fancy modes. Notice that sound is still happening.
You may not like that so just type '$ myClock.runmode('none', False, False)' instead
to turn off all the sound."
$ myClock.runmode("none", False, False) #mode, second hand sound, chimes sound
###Digital Clock###
$ myClock.analogue = False
$ myClock.mil_time = True
$ myClock.set_time(0,0)
$ myClock.second_hand_visible = False
"Now let's switch over to Digital mode--because this clock can be digital or analogue!
Let's start by setting everything back to zero and making sure we are in military time."
$ myClock.set_time(0,45)
$ myClock.set_alarm(1,0)
"Here is the digital clock set to 00:45, but I've turned off the visibility of the seconds ...since most digital
clocks don't actually show seconds. I am also setting the alarm for 1 o'clock."
"A quick note, the digital version doesn't have the chimes or the second hand ticking sound available...
because digital clocks don't make old school ticking and chiming sounds."
$ myClock.add_time(0,5,1)
"Five minutes pass...with animation, it should take a second."
$ myClock.add_time(0,10)
"We jump ten minutes with no animation...we should hear the alarm go off."
$ myClock.add_time(1,0,3)
"Let's forward the clock an hour...It should take 3 seconds."
$ myClock.sub_time(0, 20, 2)
"Now let's subtrack some time."
$ tp = myClock.time_passed(0,45)
"By the way, how much time has passed since our starting time of 00:45? [tp] minutes!"
$ myClock.set_time(12,59)
$ myClock.set_alarm(13,0)
$ myClock.second_hand_visible = True
$ myClock.runmode("auto")
"Okay, that was fun, but let's start starting at 12:59 and including visible seconds. If you wait a short
bit, you should hear the alarm."
$ myClock.set_time(23,59)
$ myClock.set_alarm(0,0,False)
"Okay, that was fun, let's do it again with alarm off so no alarm should ring."
$ myClock.add_time(0,10,2)
"Ten minutes pass...in auto run mode."
"I just wanted to make a quick note: Rollback/Saving will work in all modes but real time mode...
because in that mode the clock is always running off of real world time!"
"And now we are going to realtime mode."
$ myClock.second_hand_visible = False
$ myClock.runmode("real")
"Here we are in the real time clock function. But let's also take away the seconds."
$ myClock.second_hand_visible = True
"So the real time clock is running now, but now with more second hand action!"
$ myClock.add_time(0,10)
$ myClock.add_time(0,10,3)
"Ten minutes should not pass here...because we are connected to real time."
$ myClock.sub_time(0,10)
$ myClock.sub_time(0,10,3)
"Ten minutes should not be subtracted here...because we are connected to real time."
$ myClock.runmode("none", False)
"Now I've turned off the clock."
"Well, that is the entire clock tutorial. I hope it is useful to you!"
"Bye!"
hide screen clock_screen
with Dissolve(1)
return
I hope this was useful to new folks from a new folk! If any of you veterans have things you want to add, please feel free!
I'm uploading the files below, in the form of a Ren'Py project for your perusal!--it includes my sort of sad image files. Enjoy!
Final Note: In case you didn't know it, Renpy is based off of Python 2.7, so if you want to look up Python tutorials on the web, that is the version to go with. The official Python documentation is here in case you want to explore: http://docs.python.org/2/index.html