whydoitweet

I'm a Lecturer in Computer Science at Brunel University London. I teach games and research simulations. Views are my own :).

In this installment of Coil-monetized Brython game development, we will be adding tile images. We are building on the previous tutorial, where we added support for touch screens and mouse handlers, but where the tiles were still rather basic colored squares.

Intro: it is a good day to be monetized!

It seems like ages ago, but back in December I made a first simple demo that combined a Brython-based browser game in repl.it with Coil monetization. Now coding in Brython can sometimes be a bit harder than other platforms, so it's good to remind people why it's worth it.

Essentially, when users open your Brython game and play it, the Coil ticker starts counting just like it would for a Coil blog post, a Cinnamon or monetized YouTube video, or for a Simmer.io Unity-based game (yes, life is indeed much easier if you want to monetize a Unity game, but I'm unfortunately a hardcore Python fan when it comes to my hobbies, hence this blog series ;)).

All this is already great, because you can get some monetary rewards for your creative efforts without going through (a) the Seven Layers of Sales Marketing Hell or (b) the Nine-Hundred-Eighty-Five Layers of Finding non-Intrusive Yet Effective Advert Placements Hell.

A second benefit is that you although can receive this payment through a currency that this man has a strong hand in:

Image courtesy of *Pixabay*. It is supposed to depict the POTUS on a happy day.

...you could also opt for the XRP currency which is much less directly tied to the whims of a particular national leader. (As a side comment, someone should really fix this redirect from XRP to Ripple, which I linked to above, and make a dedicated XRP page on Wikipedia (!)).

Aside from having the vague ambition of being an early adopter, I especially like this alternative because I'm not so impressed by the leaders of the major countries in the world today ;). And if I am to believe the local Twitter bot about XRP/fiat trading going on (who goes by the artist name of @LiquidityB), then it seems I'm part of a growing group these days.

Anyway, enough blather about the payments, onwards to the game development!

Step 1: Getting an image of the tiles

I will cover tile making separately in a future installment, but for purposes of this tutorial, I will simply reuse this image with 48x48 tiles that probably last looked cutting edge in 1992 ;):

Quite importantly, in this tutorial we're going to number the tiles from 0 all the way to 39, with the first row being 0,1,2,3, the second row 4,5,6,7 and so on.

Great, that was quick then! On to step 2:

Modifying the tile rendering process

Just as a reminder, we are doing this tutorial in a series, so it's probably easiest to fork my GameWorld 7 repl.it, and take that as a starting point. All the changes we do in this subsection will be in Tiles.py.

The first thing we need to do is to load our image into the repl.it. To do this, we need to upload the image to the repl.it server's main directory. After that, you add the following line to the initialization function of your Tiles class (called __init__()):

self.tile_img = html.IMG(src="tiles.gif")

This ensures that the gif image with all the tile graphics is available within the Tiles class :). Note that you can also use other formats, such as .jpg (not recommended) or .png (definitely recommended!).

Now, we of course need to display the tiles in the appropriate way. To do this, we write a brand new show() function for the Tiles classs. We do that as follows:

def show(self, screen, x, y, width, height):

We start of by (again) defining the tile range in x and y direction that will be shown on the screen:

txstart = int(x/self.tilesize)

txend = int(math.ceil((x + width) / self.tilesize))

tystart = int(y/self.tilesize)

tyend = int(math.ceil((y + height) / self.tilesize))

We then iterate over those two tile ranges to draw each of the individual tiles:

for i in range(txstart, txend):

for j in range(tystart, tyend):

We obtain the coordinate for the top left coordinate of the tile to draw:

xtile = i * self.tilesize - x

ytile = j * self.tilesize - y

And the index number of the tile we wish to draw.

tile_num = self.tiles[i][j]

As a reminder, we count tiles from left to right, and then from top to bottom. So the tile_num is 0 for the top left tile, 1 for the tile to the right of it, and 4 for the leftmost tile on the second row. Lastly, we draw the tile image, by selecting the right region in the tiles.gif image, and rendering that to screen.

screen.drawImage(self.tile_img, (tile_num%4)*49, int(tile_num/4)*49, 48, 48, xtile, ytile, 48, 48)

To be exact, the region we select starts at x = (tile_num%4)*49, y = int(tile_num/4)*49, and is 48x48 pixels in size.

Now, with all these things in place, we are ready to render images!

Making a tile group

Now the graphics look a bit better than they did before, but it's still tedious to manually insert tiles with statements like:

tiles.tiles[4][4] = 0 # a small pond

Fortunately, this can be solved very easily, and in this last part I'll explain how to do so.

In your main.py file, we currently do the tile drawing, but we can make this easier with functions that create groups of tiles. As an example, a group of tiles could be a house, a lake, a wall or a very large tree.

As a primer for you I'll present a simple tile group function to draw the insides of a house. At a later stage I'll show how to make a roof and other things, because that aspect requires you to learn about multiple map layers (and create them ;)).

Anyway, here is an example of a house tile group function. The function is called makeHouse() and it takes the x and y position for the house, as well as (optionally) its width and height:

def makeHouse(tiles, x, y, w=5, h=5):

# this only makes the inside for now.

h -= 1

# back wall

tiles.fill(8,x,y,w,1)

# front wall (with opening)

tiles.fill(8,x,y+h,int(w/2),1)

tiles.fill(8,x+int(w/2)+1,y+h,w-(int(w/2)+1),1)

# side inside walls

tiles.fill(7,x,y,1,h)

tiles.fill(7,x+w-1,y,1,h)

# floor

tiles.fill(11,x+1,y+1,w-2,h-1)

By using the variables x, y, w and h in the tile fill functions, we've been able to create a house that dynamically resizes and repositions, based on the values we provided to the function. This is great, because we can now call the function twice to make two differently-sized houses in two different locations :), e.g.:

makeHouse(tiles,4,3)

makeHouse(tiles,13,5,w=8)

...and that's all you need. If you put all the code in correctly (and I didn't make a mistake here in the tutorial), then you should get something like this:

Closing thoughts

It's all still very basic, but with mouse movement, animated sprites, text boxes and now tile images and simple houses, I hope you're starting to recognize the outlines of an RPG-style game :). There are so many things to still work on, such as making better tiles, adding layers, adding obstacles or introducing play actions other than talking. My hope is that we'll get around to all of that in not too long :).

Anyway, here is the link to the GameWorld 8 Repl:

https://repl.it/@djgroen/GameWorld-8

And here's a direct link to the game:

https://gameworld-8.djgroen.repl.co/

(monetized to my Coil account, so please fork the Repl and change the Coil pointer if you don't want to sponsor me ;)).

Lastly, for the subscribers I have a little bit more about my view on XRP, and why I chose the angle I did today.

Read more...

Hi everyone.

As you may have noticed, I have been posting less than usual as of late. I am indeed in a very busy period at the moment, both in terms of work and in terms of household organization. As a result, I'm struggling to find the creative mental space (and time) to put together new blog posts.

It may take a few weeks for this busy period to subside, so I hope you can bear with me while I am slushing through my workloads and revive my creative juices again in this winter period..

In the meantime, enjoy this picture of what is perhaps the most beautiful supercomputer in the world.

(I've been quite close to it today, as I was examining a PhD student who ran simulations of composite materials on this thing :))

Credits

- Header courtesy of Hokusai

- Supercomputer photo from MareNostrum courtesy of the Barcelona Supercomputing Centre.

Read more...

Apparently, the average worker spends a good 2.5 hours per day responding to e-mails. So time to talk mail today.

Now my daily mail is of better quality than The Daily Mail most Britons read (I wouldn't want to direct traffic directly to the Daily Mail, so rest assured about the destination of that link), but nevertheless some mails are clearly better than others.

Today, I'll take a selection of common title elements, and share with you how I look at these titles. I do not have the slightest shred of evidence that my interpretation is correct, or even common, and I certainly will be the last to claim that I'll provide “excellent subject lines guaranteed to get your message read” like Business Insider does. As an aside, please don't be put off by that terrible title, as that article does get much better further down with scintillating suggestions such as “Always write a subject line”.

With the bar set at such dazzling heights, please humor me in my attempt to try and top that article in terms of usefulness:

1. The Power of the Colon

...by which I mean the punctuation mark, of course. Like many things in life, e-mails need to rest on a solid fundament, which is exactly what the colon provides. In my opinion, colons come in three types: the good ones, the bad ones and the ugly ones. Good ones are preceded by an short important few words, and followed by a longer descriptive title. Here's an example from my inbox:

  • Reminder: Test M18 VECMAtk tool and fill the questionnaire by next Friday, 24 January.

Other nice prefixes in my opinion include:

  • “Doodle”: a prefix which means you click a link, check a few tickboxes and continue with your working life without further agony.
  • “Decision”: a prefix which announces a dilemma, with its difficulty being dependent on why sent it...
  • “Action”: a prefix indicating I need to do something.
  • “FYI”: disliked by many, but if used correctly I think it is useful. For me it's a signal that the e-mail is not important but interesting, and I can easily choose when to read it.
  • “Unimportant”: a prefix which draws leisurely curiousity. It's like a micro-holiday in your inbox!

Some bad prefixes may include:

  • ANY PREFIX IN ALLCAPS: as it draws a level of annoyance which is inversely proportional to the importance of the actual e-mail. So if you don't want to drop down in people's priority reading order, then avoid all-capping e-mails that are not important...to them.
  • “Please Read”: a prefix which is bad because you realize it wasn't worth reading once you were two words in.
  • “INFO”: frequently sent by official organizations, this prefix indicates that the e-mail has information in it. If used consistently by everyone though, I fear my mailbox would collapse into a singularity due to excessive information prefixes.

And then there are the ugly prefixes, which are often unintentional and tend to look like:

  • “Re: Antw.: Re: Antw: Re: Re: Re: FW: FYI:”: it's like having a log dump served straight to you as a title. Potentially interesting and entertaining, but usually ugly and distracting.

Alternatives to the Colon

Some senders prefer to use the semi-colon (;) instead, which gives less of a break and also is less noticeable, or even the hypen (–), which really doesn't stand out in a title – but is not the worst choice if surrounded by spaces. E-mail titles tend to get patchier when they contain a question mark (?), which unquestionably draws excessive attention accompanied with annoyance, or the comma (,), which just makes the title look ill formatted.

2. The Rest of the Title

Right, there is more to a title in e-mails that just the prefix. Now going through that altogether may be a bit too much, but let me share a few of my tips:

  • Length: shorter is normally better, and for sure anything beyond 60-80 characters will get chopped off on most platforms.
  • Deadline dates are great to have in titles, but keep them short (e.g., “Feb 3” instead of “Tuesday February the Third”) & make sure you don't set excessively early ones (see my earlier post).
  • Keep titles consistent and predictable. This will train your readers in giving your e-mails the priority they indeed want it to have, avoiding annoyance or accidental ignorance.
  • If you're in a hurry, write in the title what you need. If you have time and want to make the mail easy to process, write in the title what you'd like to show...
  • ..but avoid using filenames in titles. It makes it look like you're using e-mail as a poor-man's version of Google Drive or GitHub, and could be caught out by spam filters.
  • And avoid attention hooks, like “don't forget” or “please check”, when they are not strictly necessary. The reminder prefix mentioned earlier is actually a decent alternative if you wish to remind people.
  • Mix it up occasionally with regular recipients. Work isn't meant to be an infinite grind, so consider mixing up small twists or jokes (where appropriate) in 1-5% of your e-mails (ideally the less urgent ones).

With that being said, there is a lot more to e-mails than the title, and I will be happy to get around to that in my next installment (or the one after it) of Boffin's Office!

For subscribers I'll mention a common source of poor e-mail titles.

Credits

Header image is public domain. Source: http://www.publicdomainfiles.com/show_file.php?id=13935838212028

Read more...

We have a working web-monetized game town, but not yet for touchscreen platforms. Today we'll fix that.

In the previous installment of GameWorld we ported our little map with talking characters from PyGame to Brython, enabling users to move the characters using the arrow keys. All this is great, but unfortunately phones and tablets are not made with arrow keys these days. So let's fix the controls today with a relatively straightforward tutorial.

Mouse Handling in Brython

As you may recall, we were able to register keystrokes by adding a @bind command in our main.py script just before we define the function handling the events. To be precise we did it as follows:

@bind(document, "keydown")

Now in this particular interface, touches are actually mapped to the mousehandler. To create a function that handles these inputs, we therefore need to bind the “mousedown” event to it. In our case, the following function will do quite well:

@bind(document, "mousedown")

def mouseDownHandler(e):

global dest_x, dest_y, x, y

dest_x = max(e.x+x-24, 0) # -24 to get center of sprite on location

dest_y = max(e.y+y-24, 0) # -24 to get center of sprite on location

What this does is basically storing the mouse pointer in two global variables: dest_x and dest_y. The idea is that the center of any sprite will try to travel to these coordinates, and to accomplish that we need to add offsets of -24 to both these coordinates (otherwise a corner of the sprite will touch the clicked destination instead of the center). Note that these variables provide pixel coordinates on the underlying map, not on the screen :).

These global variables can be updated at any time, and don't map to anything at the moment. However, we can access them from anywhere within the game code.

Getting sprites to move to the mouse destination

The next step is to get our sprite to move at a normal tempo towards the clicked location. To do this, we will call a function called GetUpdateMouseMove() at every animation frame. I'll go through this function in detail:

def GetUpdatedMouseMove(x, y, map_width, map_height, dest_x, dest_y):

We need to know the current sprite x and y location, the width and height of the map in pixels, and the values of dest_x and dest_y, i.e. where the mouseclick took place on the map. Next we declare three placeholdeers for the new x coordinate, y coordinate and facing,

newx = x

newy = y

newf = 0

and we calculate how far away the sprite is from its destination.

x_offset = dest_x - x

y_offset = dest_y - y

We now check whether the sprite is on the destination x coordinate, if not, we will move in the x direction first,

if (abs(x_offset) > 0):

# x axis move

...which is to the left if the offset is negative,

if x_offset < 0:

newx -= 6

(and using the next line we prevent our sprite from moving past the x destination),

newx = max(newx, dest_x)

newf = 3

if the offset is positive (else case), we move to the right in the same way.

else:

newx += 6

newx = min(newx, dest_x)

newf = 1

Now this next else statement is higher up in the code structure, and will be triggered if the x destination has been reached. In this case, we will try to move on the y axis,

else:

# y axis move

again, moving up if the offset is negative,

if y_offset < 0:

newy -= 6

newy = max(dest_y, newy)

newf = 0

and down if it is positive.

else:

newy += 6

newy = min(newy, dest_y)

newf = 2

Lastly, we return the updated x, y and facing values that were created in this function.

return newx, newy, newf

Job done, right?

Yes, we're almost there, but we still need to call this function in our main update loop, and write a handler for the movements. First, we use the following command in the main loop to do the handling:

moveHandler(mode="mouse")

This function calls a moveHandler function which I created to do unified handling of key and mouse-based movement. I'll provide is here, and highlight some key parts:

def moveHandler(mode="key", keyCode=0):

newx = 0

newy = 0

global px, py, f, show_text, npc_text, dest_x, dest_y

if mode == "key":

These next two lines are crucial: they ensure that any mouse-driven movement will cease once the player navigates using the keys.

dest_x = -1 # disable mouse movement

dest_y = -1 # disable mouse movement

And then we use our old movement handler for keystrokes:

newx, newy, f = MoveControls.GetUpdatedMove(keyCode, px, py, map_width-tilesize+1, map_height-tilesize+1)

elif dest_x > 0: # mouse movement

And in the case of mouse control, we use the function we just wrote:

newx, newy, f = MoveControls.GetUpdatedMouseMove(px, py, map_width-tilesize+1, map_height-tilesize+1, dest_x, dest_y)

else:

return

And that's all. Using this code, you can add mouse movement to any Brython game. If you'd like to see a working example, have a look here for the source code:

https://repl.it/@djgroen/GameWorld-7

And here for the Coil-monetized game itself:

https://gameworld-7.djgroen.repl.co/

I hope you found this useful, and see you next time! For members, I have a small preview of what's next.

Credits

Header image courtesy of pexels.com.

Read more...

The barrage of New Year's resolutions on Coil appears to have been modest. I spotted a fairly epic one by Scott Cunningham, and a rather comical one by Adam Waring. Unlike on Facebook or Twitter, I missed out on any Coil hordes joining for Dry January (where one abstains from alcohol), Veganuary (where one abstains from animal products, or Dry-Veganuary (a.k.a. Draganuary, where one either does both, or simply chooses to bail out altogether by cross-dressing for a month).

My resolutions are simple and (hopefully) generically reusable. If you ran out of inspiration for resolutions, and are looking for a few last-minute ones, you've come to the right place!

1. Measure my/thy cell phone usage

Tic, tic, tic, another second of mobile phone usage... Image courtesy of Bosland Corp / Flickr.

We use our smartphone too much. Some mass-media toy around with you by giving 5 signs that you are using it too much (of course any living mammal will trigger sign 2 at some point, but let's not ruin the content here...), while others like Psychology Today like to just state it for you without any hassle. There is also the occasional blog that actually makes you feel good because it tells you that other people use the smartphone a whopping 4 hours per day on average!

Of course it's all terrible, and we as a modern society resembles little more than a set of Furbys that are remote-operated by iPhones or Android KitKats. So we need to use the smartphone less. And that is where the problem comes in.

After all, how many of you ever measured your smartphone use in a systematic manner? Well, I certainly didn't. So my New Year's resolution is actually quite simple: to measure it.

I tried a few apps, and several popular ones recorded 24 hours of use per day for me. But Screen Time does a good job for me, as it has just about the right level of notification clutter to give me a little guilt-nudge, without getting to the point of me wanting to throw my phone out of the window.

2. Incorporating the notion of Caffeine Half-life

And so does caffeine apparently! Courtesy of *Chubu Electric Power Co., Inc*.*

Caffeine has a half-life of 6 hours in a healthy human body apparently. It also has a large range of negative side effects, many of which are rather subtle and manifest itself at night rather than during daytime. While I am not a coffee drinker, I do take in quite a bit of caffeine in other ways (e.g., ice tea and dark chocolate). Now I get a lot of things done with caffeine, but does that automatically mean I wouldn't get them done without it?

Since my sleep has been a bit crap in the past year, and caffeine could be related to it, I strive to remove my wilful blindness to the phenomenon of caffeine half-life. Now cutting it out altogether is too draconic for my taste, but in practical terms I plan to cease any intake of caffeine whenever I think I might be going to bed in the next 6 hours. I'll let you know how that works out over the year... :).

3. More time with my/thy significant other

In case you misheard that... Source: *Elena Roussakis on Flickr*.

My wife came back to me the other day, telling me that good couples should spend at least 15 hours with just the two of them every week, and that we should aim to do the same. Yes, that's without children, friends, parents etc. etc., though the jury is out on whether things like televisions or other overwhelming distractions count.

Now I thought I'd dig up the source for this via Google, but I instead landed on a page called “The Policy of Undivided Attention”. The page is a bit light on the sources (i.e., it has 0 non-self references) but this is made up by hilarious phrases such as:

Romance for most men is sex and recreation; for most women it's affection and conversation. When all four come together, men and women alike call it romance and they deposit the most love units possible.

Now I am sure we all want to be depositing the most love units possible in our lives, but please don't take this blog overly seriously ;). Nevertheless, there is a point in spending more time one-on-one with your partner, and I don't think we really need a pile of Nature papers to justify that. So it's indeed a good thing to aim for.

Now some couples may be able to use up their 15 hours of free childcare for this purpose, but in our case we're both full-time academics, so we'll have to try and be a bit more creative. Perhaps I can share some techniques on how to free up one-on-one time in a later blog post, but for the time being we both will happily aim for 15 hours, and will go out of our way to make at least 10 hours of one-to-one contact possible per week :).

Oh... but I hear you wondering now: how much did we spend one-to-one last year per week? It's a personal question, but my guess would be around 5-6 hours, which clearly was not enough in any case.

Closing thoughts

I appreciate that many of you may well be single (do page Dr. Nerdlove if you've an involuntary single), and that the third one might not be so relevant for you. But other than that I thought these resolutions could be interesting for others to pick up.

Anyway, see you next time! For subscribers I have a little bit more about tracking things.

Credits

Header image courtesy of pxhere.com.

Footnote

*License is unknown for this graphic, but given the public educational nature of this graphic and its source, I think few, if any, people will mind me propagating it further with a link to source.

Read more...

Before I make any new posts, I did a quick round across my existing ones, and patched them up where needed. Here is a list of the changes I have made:

Bugfixes

- Two recent Brython Repls (Small Sims 10: grow your own tree and Game World 6: starting your own town 2) broke somewhere along the line, because two function declaration statements missed colons. I made the following changes:

  • Fixed both codes.
  • Changed the canvas settings such that they look better on different screens.
  • Cleaned up the HTML index pages to further declutter the look.
  • Removed some unnecessary script imports.

- Several older tutorials used imports from the PIL (or Pillow library). However, during recent upgrades of repl.it, these imports unfortunately broke. However, by installing the Pillow package inside repl.it, it's possible to resolve this problem. I have now added these two steps to the Small Sims 3 and Game World 1 tutorials, and fixed the corresponding repls as well :).

Enhancements

- I added an index for the Small Sims series to my blog, to make it easier for new visitors to check out all the installments of the series (I thought this would be much easier than going through those old “previous”/“next” links). I will do the same in the future for any other series that ends up with more than 10 installments at any point.

Removals

I went through the blog, and currently I don't think there are any posts sufficiently useless to be removed altogether. The second post I made (about parallel tracks) comes close, but I decided to to leave it in for the time being :).

Closing thoughts

I will update this post as I make further cleanups during this week. However, don't expect anything major as I believe I've already resolved the most pressing issues.

However, if you spot any issue that you would like me to fix, please do contact me at @whydoitweet and I'll happily resolve it!

Credits

Header image by Michal Jarmoluk from Pixabay.

Here is an overview of all the posts in the Small Sims Series in my blog:

  1. Random Walk
  2. Crypto Price Chart
  3. Living Populations
  4. Bouncing Ball
  5. Pub
  6. Stars and Planets
  7. Election
  8. Vibrating Guitar String
  9. Simple Explosion
  10. Simple Growing Pine Tree
  11. Epidemiological Model

I will keep this index updated in the future, as I will be adding new installments to my blog.

Credits

Header image courtesy of Nick Youngson CC BY-SA 3.0 Alpha Stock Images.

The human brain urges to wrap a single narrative around periods in the past. But 2019 is a year of many stories for me. Let me share some of them with you here.

2019 in my life

I'd like to keep this one brief here: since the birth of my fist daughter (I have two: 4 and 2 years old), this was the first year that I felt life was actually a little bit easier than it was the year before. My work load still peaked, my social life is still at a crawling pace and my cultural intake is bare-bone on the best of days. But there was a tiny bit of extra room for everything this year. And that helped make this year be a lot more pleasant than the last one.

That being said, there is a massive backlog in many areas of my life, and simply catching up on all of that anytime (let alone anytime soon) is probably not realistic.

2019 in the world

I'm sure you know where this is going... Source: Deal or no Deal show.

As far as I could tell, living in the UK this year was the year of screaming politics, like the previous two. With a general election and never ending Brexit Deal/No-Deal drama, our nerves were tested to an extended stretch. And yet in the UK I also sense that people are getting exhausted by politics (more on that on my 2020 post later).

It was also a year where reality frequently failed to neatly line up with people's wishful thinking (e.g., Brexit will be reversed, the crypto world will boom). Many of us didn't want Boris Johnson to win the general election, and similarly many of us wouldn't want Donald Trump to win the next election in the US. But although we can play our little parts and stride for the causes we support, it doesn't mean at all that reality will bend to our will. I think it's as important to handle bad outcomes with wit and diligence, as it is to try and make good ones happen.

2019 on social media

TikTok was a flop... source: Axiom.com

At the start of the year I decided that I wanted to be more social on the internet, and be an early adopter on one or two platforms. I was frustrated with Facebook's filtered world, and Twitter's mile-long feed, and it was only Reddit where I could browse and write comfortably.

So I got myself an Instagram account, but thought it worked very poorly as a social media platform. I also tried TikTok, but was already scared by the sheer tedium of having to record videos on an occasional basis. But during the spring and summer I noticed Coil, and saw that the platform was making progress, so I started blogging here in August.

Quite a few posts have accumulated since then, so let me give you a few highlights here. On programming, I think the strongest three of general interest were probably these:

  • Small Sims 7 – Modelling an election: the correctness of this tutorial has now been validated with the UK General Election 2019, where the Conservatives took 60% of the seats with 44% of the vote ;).

  • Game World 6 – Starting your own Town 2: An open soruce walking environment monetized with Coil. This will be the starting point of much more in 2020.

  • Game World 1 – Making a custom animated globe: it's a shame the PIL library import in repl.it is currently not working, but I think this tutorial is one of my favorites. It's very generic and re-usable for many purposes, but also fitted really well as a start for this series.

And outside of the programming topic, I think these are probably the nicest to look back on:

  • The list of microscopic office crimes 1 and 2: a lot of my colleagues, both inside and outside of academia, responded with clear recognition of their sins, so I suppose I was not alone ;).
  • My first year on the internet: This was my favorite from the Blast of the Past series. It used to be subscriber only, but especially for this review I made it public now.
  • Report on the aftermath of an extreme election and conference week: the general election had a very big impact on our lives and my workweek was ridiculous in that period. However, it really helped to blog a bit during it.. #CoilTherapy

Thanks to the blog I enjoy using Twitter and Facebook much more now, using my blog posts to foster some new discussions. As for TikTok and Instagram...I suppose they don't fit my social media taste very well.

Closing thoughts

2019 was a decent year for me. I hope you'll enjoy the tail end of it! For subscribers I'll share a few last very short findings from the year.

Oh, and I will select an end of year song.... but I haven't picked it yet! (I'll update this post when I do).

Credits

The header image is a picture of a taxi in Addis Abeba, taken in July by myself. It's driver was a huge guy in an orange bomber jacket, by the way.

Read more...

Today we'll grow a simple pine tree, in line with the theme of the season ;).

Although there are many old papers on how to simulate tree growth with a lot of realism, I decided to initially go for a quite simple, cartoony look. Once I've bolstered my drawing library a bit more, I'll be more than happy to go more advanced on this at a later tutorial though :).

Anyway, for this short tutorial we'll use Brython, like we did in the previous short tutorial. You can create a Brython Repl by visiting https://repl.it and making a Python Repl. Inside it, you then create a file called main.html, which is initially empty.

Creating the Canvas

To create a canvas, you should put the following HTML code in main.html:

<!DOCTYPE html>

<html>

<head>

And on the next line, either insert your Coil pointer, or leave this line out altogether :).

<meta name="monetization" content="<YOUR COIL POINTER!!!>">

...and after that line, you can paste in the rest:

<script type="text/javascript" src="https://cdn.rawgit.com/brython-dev/brython/master/www/src/brython.js"></script>

<script src="https://cdn.rawgit.com/brython-dev/brython/master/www/src/brython_stdlib.js"></script>

<meta charset="utf-8">

<meta name="HandheldFriendly" content="True">

<meta name="MobileOptimized" content="320">

<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no" />

<title>Small Sims 10: Growing a Pine Tree</title>

<link href="style.css" rel="stylesheet" type="text/css" />

<style>

html, body {

width: 100%;

height: 100%;

margin: 0px;

border: 0;

overflow: hidden; /* Disable scrollbars */

display: block; /* No floating content on sides */

}

</style>

</head>

<body onload="brython(1)">

<canvas id="myCanvas" width="640" height="480" style="width:640px;height:480px"></canvas>

<script type="text/python" src="main.py"></script>

</body>

</html>

This creates a near-empty page with a small canvas where we will grow our tree. Of course, you can easily embellish this to make a Christmas card, using your elite HTML coding skills (if you're old enough to have them ;)).

Getting some drawing functions

Just to make this tutorial more accessible, I prepared a very simple library with drawing functions for you. To include it, please download the following file:

https://brython-christmas-tree--djgroen.repl.co/draw.py

and upload that into the base directory of your Repl.

The functions are nothing special, just shorthand commands to draw lines, circles, rectangles and triangles (and to fill them with a color).

Initializing Brython

To initialize Brython, you will need to create a file called main.py. At the top of the file, you'll then add several code fragments. First, we add some essential basic imports:

import time

from browser import document, bind, window, html

We then import the code from the draw.py file that we just uploaded,

from draw import *

and next we add some code to get the canvas, and extract some basic properties:

canvas = document["myCanvas"]

screen = canvas.getContext("2d")

scaling_factor=1

screen.scale(scaling_factor,scaling_factor)

width = canvas.width/scaling_factor

height = canvas.height/scaling_factor

Please note that we'll do this initialization probably many times in future tutorials :).

Tutorial-specific setup

Next, we'll set up a few functions specifically for this tutorial. We first make a simple function that allows us to animate things (I introduced a variation of this in my last post).

def updateFrame(t, frame):

t += 1

if t%4 == 0:

frame = (frame + 1) % 42

return t, frame

Please note here the “% 42”, which means our animation has 42 frames. I chose this number because it is universally known to be perfect.

And next, we declare some variables that we need for this animation:

gametime = 0

frame_timer = 0

frame = 0

And lastly, we make a Panel class which also needs a bit of initialization:

class Panel:

def __init__(self, canvas):

self.t = 0

Generating the Tree

To generate and draw the tree, we need to add a second function to the Panel class. We'll call it drawTree:

def drawTree(self, *args):

Please note that this function needs to have a *args argument, as we will call it from the main document (this is a Brython/JavaScript peculiarity).

We also use 3 aforementioned global variables in this function:

global gametime, frame_timer, frame

And we will run the drawing part of the function no more than 40 times per second:

if time.time() - frame_timer > 0.025:

self.t, frame = updateFrame(self.t, frame)

screen.clearRect(0, 0, canvas.width, canvas.height)

Our trunk height starts at 50, and grows by 10 pixels per frame,

trunk_height = 50+frame*10

and we draw it using a simple line, with colors [64,16,16], which corresponds to a dark brown,

Line(screen,250,499,250,499-trunk_height,[64,16,16])

Next, we are going to make some branches! We'll try not to make branches where there's no trunk (though in my rush I'm sure it's at least slightly off ;)),

for i0 in range(0,trunk_height):

i = 420 - i0

And every branch will be 20 pixels high,

h = 20 # branch height

while it's width depends on:

  • how far it is up the tree (the higher up, the lower i will be).
  • the current frame (the higher, the wider, with 42 in the divider).
  • A constant width multiplier (I chose 4).

This results in the following function:

w = (i*frame) / (4*42)

Now, given all that, we can draw branches using this simple function:

if i%h == 0:

fillTri(screen,250,i,250-w,i+h,250+w,i+h,[16,192,16])

The color [16,192,16] corresponds to a dark green, and in general “fillTri” simply creates a filled triangle :). Note that it draws a triangle from (250,i), which is the top, to (250-w,i+h), which is the left bottom corner, to (250+w,i+h), which is the right bottom corner.

And lastly, we need to update the frame_timer,

frame_timer = time.time()

And make sure this whole thing keeps being updated!

window.requestAnimationFrame(self.drawTree)

Lastly, we need to run the actual code, which is two more lines in this file:

panel = Panel(document["myCanvas"])

panel.drawTree()

Closing thoughts

Now you have yourself a basic tree that will look like this, but animated:

Of course, I'd like to show you what the animation looks like if you've done it the way I intended:

https://brython-christmas-tree--djgroen.repl.co/

For your convenience, I've also attached the source code and the Repl. And for the subscribers I have a tiny bit about decorations.

Oh, and if you prefer to make a tree (and Santa) by hand, perhaps have a look at this nice Pixel Art tutorial :).

In either case, enjoy your holidays!

(Back to Small Sims Index)

Appendix A: Source code and Repl

You can find main.py source code here (it uses a simple library called draw.py):

import time

from browser import document, bind, window, html

from draw import *

canvas = document["myCanvas"]

screen = canvas.getContext("2d")

scaling_factor=1

screen.scale(scaling_factor,scaling_factor)

width = canvas.width/scaling_factor

height = canvas.height/scaling_factor

def updateFrame(t, frame):

t += 1

if t%4 == 0:

frame = (frame + 1) % 42

return t, frame

gametime = 0

frame_timer = 0

frame = 0

class Panel:

def __init__(self, canvas):

self.t = 0

def drawTree(self, *args):

global gametime, frame_timer, frame

if time.time() - frame_timer > 0.025:

self.t, frame = updateFrame(self.t, frame)

screen.clearRect(0, 0, canvas.width, canvas.height)

trunk_height = frame*10

Line(screen,250,499,250,499-trunk_height,[64,16,16])

for i0 in range(0,trunk_height):

i = 420 - i0

h = 20 # branch height

w = (i*frame) / (4*40)

if i%h == 0:

fillTri(screen,250,i,250-w,i+h,250+w,i+h,[16,192,16])

frame_timer = time.time()

window.requestAnimationFrame(self.drawTree)

panel = Panel(document["myCanvas"])

panel.drawTree()

And the Repl is here: https://repl.it/@djgroen/Brython-Christmas-Tree

Read more...

Remember our little town? Now it's monetized with Brython...

Well, it took a while, but I now managed to recreate the GameWorld 5 tutorial in Brython, so that you can run it in the browser, and hooked up with Coil! :)

You can find the Brython version here:

https://workspace-for-brython-game.djgroen.repl.co

And you can find all the source code and the Repl here:

https://repl.it/@djgroen/Workspace-for-Brython-game

Now you can freely fork the code and modify it, but of course you should change the Coil pointer to your own when doing so (see my post on the Brython Experiment to figure out how to do that).

Brython vs PyGame: the outcome

So in my attempt to recreate this PyGame setting in Brython, I got a good overview of how the two stack up. Brython basically:

  • Provides clearly better performance. This is because it's Cython-based (which is a C implementation of Python), and because I *think* more code executes on the client side rather than the server-side.
  • Allows monetization using Coil of course :).
  • Allows for a user-only context, where the code can be run in the browser without all the repl.it stuff around it.

However, Brython does have disadvantages too, namely:

  • It's much harder to import external Python libraries that are not part of the core Python (e.g., numpy, pillow or indeed PyGame).
  • It relies on Cython syntax, which is a little bit clunkier than Python. For example, you need to declare at the start of a function whenever you use global variables (using e.g. `global x`).
  • The key handlers and rendering functions are different and as a whole slightly clunkier. However, I plan to make nice and clean wrappers for those as part of future blog posts :).
  • ..and this one is unfortunate, but Brython has MUCH worse error reporting. Because it's a Python implementation in JavaScript, you can unfortunately end up with JavaScript errors which are extremely hard to diagnose. On two occasions I simply had to revert to a last working version in order to solve bugs, but fortunately Repl.it allows users to easily do that. If you ever need to do it, just click on the little whirl next to the word “saved” here:

A fork in the road

Going forward, I plan to split the GameWorld series into two. One series will still be about designing and implementing your Game World, and will be focused on the educational aspects of it all. And the other series? Well, I'll use Brython to implement an RPG, and I will present key updates and new entry points to the game as part of my Coil blog updates. And, since my last game was called “Shiny Sword” and I really want to emphasize the rather serious nature of this game, I will call it Briny Sword!

Last but not least

I wish you all a Happy Christmas / Winter Solstice or whatever holiday you seek to celebrate this week! Oh, and I added a few extra characters and basic animations to my GameTown example, so that it looks slightly nicer than the one in the last tutorial.. :).

See you next time!

(for members I share a few more technical details about the re-implementation...)

Read more...