
It was done entirely in CS2D. Just watch the video.


The first mention I read about "bad apple" was in a Russian games magazine "Igromania" (Game-mania). A small column, a few lines of text were dedicated to a recreation of Bad Apple animation in WarCraft 3 using the in-game map editor. I had literally NO IDEA what this "Bad Apple" was all about and I only knew WC3 because it still was a trendy game (and esports) at the time. Yet the little black & white thumbnail of Bad Apple stuck into my head.
It was only years later that I had found the original on YouTube and watched it - no wonder it has become popular because it really is a masterpiece! There're song covers in many languages, there's an osu! map for Bad Apple and there are of course more fan-made recreations in games, the CS2D version now being one of them.
How did I come up with the idea to do this in CS2D? There must be an inspiration or purpose for everything we do and my inspiration was the Darude Sandstorm in Factorio video. Usually Youtube's recommendations suck, but this time they had helped me

So, I had an inspiration, a memory from the childhood and my favourite game at hand. There's no way I wouldn't try to replicate it in CS2D. And I did.

Luckily, I can play this creative game called "programming". The goal is to somehow render video frames in the game. However, CS2D has A LOT of limitations and the biggest one to overcome is

20x15 tiles? That's just 20x15 PIXELS! It's nowhere enough to create a picture!


Screw that. 20x15 pixels is for n00bs. We are f***ing evil genuises here and will not stop. How do we make more pixels out of that? Right, launch MOAR cs2d windows.
Many people don't even know that it is possible, but the game window you see doesn't need a surrounding border at all! This way we can disable the window borders, move different cs2d windows close to each other and it will look like a huge single game window that we can draw pixels inside. (that window mode is called "borderless", maybe you have seen the mode "borderless fullscreen" in some newer games).
At this point I had to do some easy peasy math. I have a 1920x1080p monitor and by experimenting I found out that I can reliably run up to ~28 copies of the game. The Bad Apple video has a 4:3 screen ratio, same as the the old cs2d version 1.0.0.2. This results in a 5x5 game window grid or 20x15 * 5 = 100x75 pixels. Now that's something we can work with!
To accomplish that I wrote a script in AutoIt that would start 25 cs2d windows, move them to a correct position and then they would connect to the server as players. Our canvas for drawing is ready!
One thing that I didn't expect to happen is that my computer's performance was not enough to handle all those cs2d windows



Anyway, it worked. Let's proceed to...

CS2D uses the Lua programming language to script mods etc. It will be running on the server and tell our little player-windows what to do.
The most important part is to spawn each player at a correct position, from top-left to bottom-right corner.
I had to disable






Next up is the hardest part. We have a 100x75 map size, but how are we going to draw the pictures?
The boring way is to save the video as single images and show the whole image to a player (640x480 window resolution) - but it sucks, because it's too easy. We would not need 25 windows for this. Everybody can do

Another way is to use

Third option is the same as with

I have worked with

Next I wanted to test drawing performance. How many tiles can I change each frame at 50 FPS? The result was disappointing (bad). With 25 players and 50x20=1000 tiles per frame the players would immediately lose connection to the server and timeout. Even though the server was still running, it was VERY annoying, because it takes 6 minutes(!) to (re)start all 25 player windows.

Through further experimentation I found out that:


The dedicated server would timeout players immediately with "MAX RECONNECTIONS ATTEMPTS 20 reached" whereas the "New Game" server would have no problems.


If you ever have watched a video on Youtube (and I certainly know you have, at least since you have watched the CS2D Bad Apple) then your browser has read and understood one of the most common video codecs and containers. Youtube now delivers .webm (VP9 codec) and .mp4 (H.264) for video. Your browser is smart enough to play them, and our simple Lua script is definitely not. Lastly, it's not easy to write a script for a video format.
How can we make our job easy? Give our Lua script something it can understand! We can convert the video into a series of uncompressed images: Bitmaps (.bmp). Lua will happily eat all of them bitmaps

I've used some command-line magic to convert the video using FFmpeg. If there's a video format that FFmpeg can't understand then nothing can. It's a damn Tyrannosaurus Rex of Video.
FFmpeg has converted the video into 6571 images. Also we have created a color palette that was used for video convertion. We will use this color palette as the tileset for the map - each image pixel now has it's own map tile friend


Ok, we can now successfully read the images and draw correct pixel colors on map (the pixel color -> tile mapping has took me a few tries of trial&error, because I'm so lazy...

I first wrote functions to draw a single image on map and then on top of that, a continious loop that would draw consecutive (next) frames when the last has finished.
One optimisation I made straightaway was the RGB_COLOR -> #TILE lookup. Without knowing the actual values for Red, Green, Blue, the script would instantly know which tile it's supposed to represent and draw it.
At this point the first speed results were in:

51.5 SECONDS, CARL! That's just 01.9 (one point nine) FPS. Oh my God, that's slow. I feel like a pwned n00b now.
Don't give up though! The biggest improvement came from sending the

parse("settile X Y 123")per pixel, but it was SO SLOW that even a turtle would beat it at moving pixels. As you have read above, up to 800 tiles can be changed at once and that's what I did. For the game it looked like a freaking mother of text of a command:
1
"settile 27 65 239;settile 29 65 154;settile 31 65 3;..."
Another change of the same type was to "cache" current map pixel status. I mean, you don't want to do homework in the evening to find out the next day that the lesson was cancelled. Likewise here: we don't need to send a

I did some tweaks to the amount of drawn tiles per in-game frame and increased the speed to 2.9 FPS (~33.7s).
Any other tweaks within Lua itself that I tried didn't help. In conclusion: The communication between CS2D and Lua was slow, but not Lua itself. Nothing surprising, actually.

Here's a list of what's working:




Everything's ready, let the computer be a computer and record the video while we're afk-ing.

It took an hour(!) of in-game "rendering" time. During some scenes, that had many changing pixels, the image looked distorted due to high CPU usage. The game windows wouldn't update all at once - some windows had an older frame and the others already updated to the new one.
Another "feature" you can see in the image above are the actual players. I thought equipping players with stealth armor would be enough to hide them, but it wasn't. I ended up using the new mod feature to make fully transparent Terrorist skins. The players were fully invisible now.
I reduced the playback speed and gave it another try. 2 Hours now... such render, much fun. I was surprised when I came back: One of the game windows had CRASHED in the middle of recording, EXCEPTION_ACCESS_VIOLATION. Well... F* You Too!
However now I had a chance to change the converted images: the edges looked over-sharp-ed because of the resize method I had used. "Lanczos" gives poor results (in my opinion) in some scenes by oversharping the edges. I reconverted the images using the bicubic method and I liked the output.
Next try! And guess what? One of the game windows had crashed once again! Thanks, Obama!


In the process of speeding it up (to make it the same length as the original video) I learned that there's no interpolation of frames for timelapses (and it is kind of a timelapse).
Initially I thought: Let's do the recording as slow as possible, then there will be very few image artifacts and the final video will have high image quality, because we will use many "good" frames for interpolation and less "bad", distorted frames. There seems to be no such thing.
The interpolation is used to convert e.g. 30 FPS -> 60 FPS video, but no one does it the other way around. If you need to put a 9000FPS into 30FPS video, then every 299 Frames will be skipped/dropped and the 300th frame picked up. If this 300th frame is full of artifacts then you are out of luck, and this shitty frame will end up in the video.

I could record more popular videos in CS2D, but there's virtually no more purpose. I had fun. I practiced some Lua and coroutines, learned more about CS2D: Weapons have limited range! I even managed to visualize a laser shot's length. Moreover, there're many bugs within CS2D (and one in Windows' Desktop Window Manager - the startup time of 6 minutes...)

Finally, I added an outro screen with the request to vote for CS2D on Greenlight. At least something practical, although CS2D seems to do good compared to other Greenlight titles. Nonetheless, I think CS2D needs more votes than a regular Greenlight game just because of the name and the fact that it used to be a Counter-Strike clone in 2D.
Share the video and thank you for reading!
If there's public interest, I will publish the rest of the scripts and a howto on Github (right now there's only the server-side Lua script). And just keep in mind that your PC will most likely not able to handle it

I tried to use simpler English on purpose, e.g. word (explanations) - we have many huehuehue-friends here who don't speak Ingles very well.

Sincerely, VADemon.
PS:

PPS: I am tired of writing. I can haz f00d finally!
MISC:
Image album: https://imgur.com/a/1ppsC
CS2D Bad Apple video: https://youtu.be/YsRol0sV754
Bonus video: https://youtu.be/ataoxr3W4tE
Repository (incomplete): https://github.com/VADemon/cs2d-script/tree/master/badapple
---
TODO: Report the networking bug, list of bugs
edited 7×, last 10.05.17 02:01:32 am