Deleting old articles, refactoring articles and re-formatting some older ones. Generally getting things into order. Publishing this version
parent
68010226f8
commit
c12a6259c7
@ -1,14 +0,0 @@ |
||||
Title: An open source GPU |
||||
Category: OldBlog |
||||
Tags: OpenMPU, matrices, opengl, hardware |
||||
|
||||
|
||||
For as long as I can remember I've wanted to build my own computer. Sorta from scratch. |
||||
|
||||
And there is something I want to do even more: build a GPU. Something that can process vectors and matrecies in an efficient manner, build a graphics rendering pipeline in hardware. Sorta from scratch. |
||||
|
||||
And that's what this project is about. OpenMCU stands for "Open Matrix Computation Unit" and will be a part of the OpenGPU (working title). |
||||
|
||||
## The basic architecture |
||||
|
||||
`<insert picture>` |
@ -0,0 +1,20 @@ |
||||
Title: Chaos Communication Camp 2015 |
||||
Category: Blog |
||||
Date: 2015-08-25 15:30 |
||||
Tags: /dev/diary, ccc, c3 |
||||
|
||||
Hey everybody, long time no read. |
||||
|
||||
As I returned from vacation on the Chaos Communication Camp 2015 (Not sure if I'll post more about that) and probably starting a new job next week (*pssst* not sure if I should talk about it 😉 ) the rest of my summer is still ahead of me and I'm booming with ideas and inspiration to do stuff. |
||||
|
||||
I've started more intensively coding on the `newdawn` branch of Reedb, the C port of the database and planning some features for the old codebase via the `backports` branch. Because the new codebase will use a different crypto backend (from OpenSSL to gnu_crypt) a migration agent will be neccesary to migrate between 0.11.x to 0.12+ vaults. But as very few people currently use Reedb and most setups are for testing purposes only that isn't a very big priority right now. Depends on how the current version of reedb develops :) |
||||
|
||||
But that's talk for another day. What else has been going on? After the Chaos Communication Camp 2015 I've been playing around a bit with my rad1o badge. |
||||
|
||||
![Rad1o Badge](/images/rad1o_badge.png "Rad1o Badge") |
||||
|
||||
But not much has resulted from that yet. The distribution I'm using (Fedora 22) at this time unfortunately has a broken arm-gcc package which means that a linker for embedded systems isn't working properly. So hacking on that will have to wait a little bit. But I will very likely post more stuff about that in the future. |
||||
|
||||
|
||||
Until another day, |
||||
Kate |
@ -1,7 +1,7 @@ |
||||
Title: Rebuilding my Website (again) |
||||
Category: Blog |
||||
Tags: /dev/diary |
||||
Date: 2018-01-25 |
||||
Tags: /dev/diary, meta |
||||
Date: 2018-01-3 01:31 |
||||
|
||||
It's winter, rebuilding my website is a tradition...right? **Happy new year everybody.** |
||||
|
@ -1,57 +0,0 @@ |
||||
Title: 01. Omnitool - Introduction |
||||
Slug: 01-omnitool-introduction |
||||
Category: OldBlog |
||||
Tags: Dev Diary, Hardware |
||||
Date: 17-09-2015 15:45 |
||||
Status: published |
||||
Illustration: omnitool_background2.jpg |
||||
|
||||
Any good fan of the game, book, comic, stuff series will now jump up and down in excitement. The omnitool is a constant companion, helper and friend for Commander Shepard and a life saver in several situations. Whether it be hacking doors, turning into a plasma blade and cutting peoples faces open or just plain transferring a bribe credit to the slimy Vorcha in front of you. |
||||
|
||||
But please, settle down, I haven't invented holographic technology. Nor have I invented plasma tubings or even solved the financial crisis by coming up with a perfect currency (of couse generically called 'credits') that everybody wants to use. |
||||
|
||||
### Then why waste your time |
||||
That's a good question :) I would hope that this series doesn't turn into a waste of time for anyone. Because...well, while I haven't done any of those things. I am planning on building an omnitool. Just a bit more low-tech. |
||||
|
||||
I was actually inspired by something on Adafruit, called the Flora. It's a round gimmick with a ring of RGB LED's, GPS (I think) and an arduino to program it. |
||||
|
||||
![Adafruit Flora](/images/flora_pinout.png "Adafruit Flora") |
||||
|
||||
I was only really inspired to do this project when a friend of mine showed off his Flora on the CCCamp2015. |
||||
|
||||
He did some minor modifications to it, including a wristband (IDE cable) and a battery on the underside and programmed a few modes for displaying time (as an analogue clock) and a flashlight by just dialing the LED's up to full power. |
||||
|
||||
![Adafruit Flora 2](/images/flora_withleds.jpg "Adafruit Flora2") |
||||
|
||||
And that's kinda what gave me the idea for an omnitool. The idea of circular rings of LED's as display elements are pretty cool. |
||||
|
||||
### Basic concept |
||||
|
||||
So the basic conceptis a simple. Create a wrist accessory with one or two LED rings (using shift register RGP LED's to display patterns, colours and different brightness settings), include a generic SOC to program, probably something single core ARM. Give it a bunch of RAM to run applications and a embedded systems linux. |
||||
|
||||
Include GPS, blutooth, a sensor package such as temperature, preasure, accelerometer, etc. |
||||
|
||||
Include extention slots where, with a simple click, the tool can be expanded to include speakers, a microphone, a bigger screen, a bigger battery, etc. |
||||
|
||||
And all this in the form factor of the so beloved omnitool from Mass Effect. |
||||
|
||||
I know this is a bit of a crazy project. And it will take months, if not years to complete. |
||||
|
||||
Because this is the thing: I want to do it all as custom cut PCB and maybe some custom cut plastic for casings. |
||||
|
||||
I've been getting into KiCad recently, with my first project the Christmas Bauble ([Click here for details](/dev-diary/jolly-christmas-decoration/)) and have fallen in love with the tool – Don't worry Ashley, not *that* much :) |
||||
|
||||
But it is pretty awesome and I urge everybody who wants to get into that sort of electronics stuff to checkout it out! [KiCad](http://www.kicad-pcb.com) |
||||
|
||||
### What to expect |
||||
|
||||
So what will this series be? (Hopefully) regular status reports about what I've been doing, writing about my experimences with the project, letting you guys know what I'm learning and generally just let people follow the project. |
||||
|
||||
All stuff about the project will be in a Github repo. From the KiCad files to the C firmware I'll have to write. Everything you would potentially need to make your own, study it and learn from it is in there. |
||||
|
||||
**[Omnitool Repository](https://github.com/spacekookie/omnitool)** |
||||
|
||||
|
||||
I hope that you follow along. And I'm looking forwards to comments from all of you. Have a lovely day and read you soon. |
||||
|
||||
~Kate |
@ -1,21 +0,0 @@ |
||||
Title: WS2812b LED breakouts |
||||
Category: OldBlog |
||||
Date: 2016-03-16 12:08 |
||||
Tags: Dev Diary, Hardware |
||||
|
||||
You all know I have a fetish for [ws2812b RGB-LEDs](http://rgb-123.com/wp-content/uploads/2013/08/PinLayout.jpg). I admit that. They're just awesome. And recently I've found myself wanting to do some projects with them (*cough* programmable alarm clock *cough*). But I dislike the strips, although they're pretty cheap they are usually just very messy and horrible. And making a PCB for every project can be weird. Especially if gigantic PCBs would be required. |
||||
|
||||
So I designed this little doodad. |
||||
|
||||
![ws2812b Single Mount](/images/ws_2812b_single.png ws2812b Single Mount) |
||||
|
||||
The idea is the following: Sometimes you just need a few ws2812b (I'm saying that word too often in this post) somewhere. But you don't want to lay a strip. Or make a big PCB for it. |
||||
So here is an alternative. Easily make ~800 of these for 25$, screw them to a surface, connect **PRETTY** wires between them, such as [Ribbon cables](http://cdn.usdigital.com/assets/images/galleries/ca-c10-f-c10_0.jpg) and boom. You're done. |
||||
|
||||
### One to rule many |
||||
|
||||
Now...I mentioned that programmable alarm clock earlier. And while I'm not quite done designing what will go into it all, I do know that I want to have a ~ 21x9 Pixel display, each individually addressable. And instead of building a way too big PCB that will be insanely expensive to manufacture...why not split them up? Then I have these "tiles" of LEDs that I screw to a backplate, wire everything together and from the outside you can't tell the difference. |
||||
|
||||
With 21:9 (Aspect ratio and pixels) in mind, this is my prototype: |
||||
|
||||
<TO BE CONTINUED> |
@ -1,89 +0,0 @@ |
||||
Title: Static sites vs Wordpress |
||||
Category: OldBlog |
||||
Date: 2015-08-14 20:59 |
||||
Tags: Dev Diary, Meta |
||||
|
||||
Regular readers will notice slight differences in my blog from the last time they were here. A lot has changed in the last two years since I started this website. Back then I decided to use Wordpress to host the stuff I wanted to write about because it offered lots of plugins and an easy to use CMS + editor to write articles. But over time I noticed that wordpress, while nice, can be more trouble than it's worth. |
||||
|
||||
So for the last couple of months I've had a look at different web frameworks. Django, Ruby on Rails and some static site generators like Nikola and now Pelican. The last one is now powering this website. |
||||
|
||||
The advantages of a static site generator is significantly less server load, simpler design and less overhead with updates and security issues. With no database to hold any kind of data and only static `html` documents being generated at "compile time" and dealt via a very simple html server using exploits or manipulating the website to gain access to my server is almost impossible. |
||||
|
||||
So...while you can read a lot about why static site generators are cool and what their limitations are, I wanted to use this article to write a little bit about the challenges that I had to deal with porting my old website to a new framework. Because I didn't want to start from scratch. I wanted to keep all my stuff. |
||||
|
||||
### Creating basic site layout. |
||||
|
||||
Most of my website are blog posts of different categories and streams. For example, I had one page that displayed all blog posts while I then had other pages that displayed only certain categories. Most static site generators don't really support that. On Pelican I am now using one category for Blogs (Blog) and then have other categories displayed on pages that I statically link in my config: |
||||
|
||||
```python |
||||
MENUITEMS = ( |
||||
('Home', '/'), |
||||
('About Me', '/about-me/'), |
||||
('Blog', '/blog/'), |
||||
('Projects', '/projects/'), |
||||
('LibGDX', '/libgdx-game-of-codes/'), |
||||
('Linux', '/linux/'), |
||||
('Teaching', '/teaching/') |
||||
) |
||||
``` |
||||
|
||||
So essentially the menu items link to the category pages that then display articles. By default these pages only displayed a list of posts so I had to modify the category template in my theme. But more about that later. |
||||
|
||||
Right now I still don't have a work-around for having different categories on the "Blog" page. But my current idea is to not link to a category page but rather a "tag" page. Then give every article that should show up on that page the "blog" tag (or use some voodoo setting that automatically adds tags to articles). |
||||
|
||||
To get pretty links you can set some options to save pages and posts under different URLs in the config: |
||||
|
||||
```pytho |
||||
nARTICLE_URL = '{category}/{slug}' |
||||
ARTICLE_SAVE_AS = '{category}/{slug}/index.html' |
||||
|
||||
PAGE_URL = '{slug}' |
||||
PAGE_SAVE_AS = '{slug}/index.html' |
||||
|
||||
CATEGORY_URL = '{slug}' |
||||
CATEGORY_SAVE_AS = '{slug}/index.html' |
||||
``` |
||||
|
||||
### Theming with [Pelican Themes](http://www.pelicanthemes.com) |
||||
|
||||
Obviously a website should be something personal. And on my old wordpress site I spent several weeks tweaking the Wordpress Theme to my liking. Static site generators like Nikola or Pelican do offer theming where people much better at writing CSS and html put together something nice. I would recommend cloning a template that you want to use (most of them are on Github) and then modifying it however you like. For example, my template and the modifications I'm making on it can be found here: [github.com/spacekookie/nest](https://github.com/SpaceKookie/nest). |
||||
|
||||
The first thing I did was tweak the dynamic content filling a bit. I mentioned earlier that I had to modify some stuff to make sure that I didn't just get a list of entries in my blog page but rather either the content or a summary of the page. With a little plugin called "summary" that becomes even nicer but I'm getting ahead of myself. |
||||
|
||||
```html |
||||
{% for article in articles %} |
||||
<dt>{{ article.locale_date}}</dt> |
||||
<dd> |
||||
<a href="{{ SITEURL }}/{{ article.url }}"><h2>{{ article.title }}</h2></a> |
||||
<p>{{ article.summary }}</p> |
||||
</dd> |
||||
{% endfor %} |
||||
``` |
||||
|
||||
As you can see you can embed data from your pelican site into HTML via curly braces, aka Jinja. So the above snippet is obviously a loop that takes an article from articles (which is provided by Pelican in my case) and then renders a "h2" link to the article with the title (`article.title`) and then adds the `article.summary`. |
||||
|
||||
If you wanted to display the entire content of the post it's as trivial as changing the summary to article.content. That's the beauty of Jinja: it's ridiculously easy :) |
||||
|
||||
### Next up |
||||
|
||||
Another thing you might wanna have a look at is the static/css folder of your theme. In the one I use there is a `nest.css` file that contains a lot of modifications to the underlying bootstrap theme. Including changing paddings, colours as well as overriding headers to not have these weird dashes (that look pretty cool for some parts. But not so cool for others). |
||||
|
||||
But that's all details then. Other things you might want to consider if you move your Wordpress blog to a static site generator is that Wordpress sets up a lot of metadata that then ends up in your Markdown files (if you choose Markdown). Which means that you might want to go through all your articles cleaning out unwanted metadata that might just screw things up. |
||||
|
||||
I'm using Sublime Text for the multi-cursor/ multi-file edits and regex searching which made editing my article metadata less of a pain in the bum. |
||||
|
||||
As for this site: I still have a lot of things to work out. For example I still don't have a projects page. Markdown is nice for writing articles but I'm thinking about adding Restructured Text (`.rst`) files for static pages. It's a lot more powerful but also more annoying to write. |
||||
|
||||
**-EDIT-** |
||||
|
||||
A quick insertion a few weeks after having created but not yet published this site. |
||||
|
||||
Alternatively, something I've now started using for my front page is a dedicated template for certain pages. You can set a template via `Template: <template_name>` into a files metadata. Then create a corresponsing `<template_name>.html` in your themes template folder. |
||||
|
||||
That way I can have special settings for certain pages without having to work with embedded if statements in a sinle html template. |
||||
|
||||
**-EDIT-** |
||||
|
||||
But for today that shall be it. I hope you like my new website. Enjoy the new comment system as well (which I just moved to Disqus because that's pretty cool). Until another day. |
||||
|
||||
~ Kate |
@ -1,45 +0,0 @@ |
||||
Title: Lonely Robot and the future |
||||
Category: OldBlog |
||||
Date: 2015-08-25 15:30 |
||||
Tags: Dev Diary, Lonely Robot |
||||
|
||||
Hey everybody, long time no read. |
||||
|
||||
As I returned from vacation on the Chaos Communication Camp 2015 (Not sure if I'll post more about that) and probably starting a new job next week (*pssst* not sure if I should talk about it ;) ) the rest of my summer is still ahead of me and I'm booming with ideas and inspiration to do stuff. |
||||
|
||||
I've started more intensively coding on the `newdawn` branch of Reedb, the C port of the database and planning some features for the old codebase via the `backports` branch. Because the new codebase will use a different crypto backend (from OpenSSL to gnu_crypt) a migration agent will be neccesary to migrate between 0.11.x to 0.12+ vaults. But as very few people currently use Reedb and most setups are for testing purposes only that isn't a very big priority right now. Depends on how the current version of reedb develops :) |
||||
|
||||
But that's talk for another day. What else has been going on? After the Chaos Communication Camp 2015 I've been playing around a bit with my rad1o badge. |
||||
|
||||
![Rad1o Badge](/images/rad1o_badge.png "Rad1o Badge") |
||||
|
||||
But not much has resulted from that yet. The distribution I'm using (Fedora 22) at this time unfortunately has a broken arm-gcc package which means that a linker for embedded systems isn't working properly. So hacking on that will have to wait a little bit. But I will very likely post more stuff about that in the future. |
||||
|
||||
Now, what was this post supposed to be about? Not Reedb. Or my new job. Or the Rad1o Badge or even the cccamp. It's supposed to be about a new software studio I created. |
||||
|
||||
### Lonely Robot |
||||
|
||||
So far we have a website at [lonelyrobot.io](https://www.lonelyrobot.io), an issue tracker at [bugs.lonelyrobot.io](https://bugs.lonelyrobot.io) and are expanding our web prescence but mostly working on projects. |
||||
|
||||
Two things that we currently have going on are an Android game called **Graviton** (which started out as a tech demo that got out of hand) and, more exciting, LRGE, the `Lonely Robot Game Engine`. |
||||
|
||||
The whole thing got started between my boyfriend and me who wanted to make video games together. And after a few months of day-dreaming, talking about ideas late at night and bitching about the current state of the gaming industry. But only after a few months of talking we actually started doing something. |
||||
|
||||
Over the last couple of months we've been working with the LibGDX framework, making some minior and other major modifications to it and writing the specification for an engine. |
||||
|
||||
We decided to use C++ for it and build it on top of SDL (the Simple DirectMedia Layer) with a very modular design which will allow for modules to be swapped in and out. |
||||
While we are still early in the planning phase of the engine it is what we want to focus our efforts on for the next couple of months, possibly the next year. |
||||
|
||||
While we do make (free) software most of our ideas are for games. Our vision is that with this game engine we will be in a position where we can create them. |
||||
|
||||
We decided to go for a self-written engine over something like Unreal or Unity because of a multitude of reasons. One of which is that neither of us are very great at blackbox development (that is the development of systems with another system that the developer doesn't fully understand). It is a problem I've always had with game modding but have also run into when playing around with the Unreal Development Kit. |
||||
|
||||
Even with LibGDX I've always wanted to understand the inner workings of the framework which in the end lead me to modifying large chunks of it. |
||||
|
||||
So that's that. I wanted to write about it here but will probably move other thoughts about the studio to my Lonely Robot dev blog on [lonelyrobot.io](https://www.lonelyrobot.io/blog). |
||||
|
||||
That'll be it for now. I have an idea for a different project brewing in my head but I don't want to talk about it for now. All you should know now is: `hardware` :D |
||||
|
||||
Until another day, |
||||
|
||||
Kate |
@ -1,53 +0,0 @@ |
||||
Title: All aboard, it's a C port |
||||
Category: OldBlog |
||||
Date: 2015-10-21 18:02 |
||||
Tags: Dev Diary, Reedb |
||||
|
||||
It's been a long time coming. This blog post, not what I am writing about. Though...what I am writing about has also been a long time coming. So in a way, yes, I guess I was right. |
||||
|
||||
### Anyways. |
||||
|
||||
Over a year ago I switched to Linux. I was a Mac fangirl before. I loved the ecosystem, the OS, loved the convenience and the idea of having a system that just *worked* and the power of a root terminal. |
||||
But something changed. The garden of bliss grew smaller and smaller and as I realised that I didn't own or understand my computer, that Apple was in charge of what icons I used on my Desktop, I was drawn towards Linux. |
||||
|
||||
I had used the OS on servers before and even my gaming computer ran Ubuntu to play the 64bit variant of *Kerbal Space Program*. But that last step...that took a bit longer. |
||||
|
||||
One of the issues I was faced with was compatibility of software. And while most of the things I used (Eclipse, Sublime Text, Spotify, etc.) were also available on Linux, one thing wasn't: my password manager. |
||||
|
||||
I shall not name it by name because I don't want to advertise a product I no longer want to use. But my password manager was a problem. |
||||
|
||||
So just over a year ago I set out to write my own password manager. > How hard could it be < I remember myself saying. Oh could I have been more wrong? Probably. But it wasn't easy. |
||||
|
||||
### The Origin |
||||
|
||||
Back in the day (and I know it's silly because it's only been a year. But still - a lot of things have changed since then) I knew Java, some Python and wanted to learn Ruby. My brother told me about a mobile framework with which I could easily make a mobile version of the password manager and I was convinced: |
||||
|
||||
I wanted to write it in Ruby. |
||||
|
||||
7 months of development, feedback, discussions in a variety of hackerspaces with an even larger variety of people later - Reedb 0.10, the first usable version of what was once a password manager and had migrated into something bigger, something much cooler than I ever thought it would be: a database. |
||||
|
||||
And as I added more and more features and this database became more and more intelligent I suffered from the limits of Ruby. Speed was terrible, packaging was practically impossible and it became obvious to me that Ruby was a language for the web (not because of the language. But rather because of the people that used it. The rails hipsters and web devs). |
||||
|
||||
After mucking around with it for at least a month, trying several build systems and desperately trying to get this application to work I had enough. I wanted to port it. |
||||
|
||||
Only around 3000 lines of code it wouldn't be too difficult to port Reedb into a different langauge. After all, most of the work had gone into the design process, not the actual coding. And while I wrestled with myself and tried a variety of languages, in the end I settled for C. |
||||
|
||||
With it came a wave of problems. Conventient datastructures that just existed in Ruby such as the Hash (`{}`) or dynamic types made it easy to prototype something and quickly work with large amounts of data. All of that was different in C. And it took me a few months to really start to understand the C ways. |
||||
|
||||
In the end I made a breakthrough with the design process when I finally discovered unions. And it's been a few weeks since. For now the C port of the project lives on the `newdawn` branch of the [github repo](https://github.com/reepass/reedb/tree/newdawn). The issue tracking has since been migrated to the [Lonely Robot Redmine](https://bugs.lonelyrobot.io/projects/reedb/issues) where you're welcome to fly by and check out the progress. |
||||
|
||||
### The Future |
||||
|
||||
I don't know how long it will take for me to finish this port. I'm making good progress, wind is in my favour. But there are still questions to be answered. Especially when it comes to the encryption of things. |
||||
|
||||
But overall I'm happy with my descision. C is definately different. But it's a good kind of different where you can feel the control you have over your code. The performance is brilliant and a pure C binding makes integrating it into other languages or writing extentions for it as easy as pie. |
||||
|
||||
I'll wrap this article up for now, it's gotten rather long. I hope that I can post updates about Reedb soon. Plus, I also have some other cool stuff lining up in the hardware section of my mad projects. I still want to do the Omnitool project and I'll definately keep that series alive. But it's a rather large undertaking. And I want to get some experience with smaller projects before I try to do something as mad as that. + it isn't exactly cheap to fuck up a prototype ;) |
||||
|
||||
--- |
||||
|
||||
Anyways, I have to sail back into the C... |
||||
|
||||
Kate |
||||
|
||||
P.S. Sorry for the bad sailing puns. I promise there won't be any more, for shore. |
@ -1,91 +0,0 @@ |
||||
Title: Getting started with XMPP/ Jabber |
||||
Category: OldBlog |
||||
Date: 2015-11-24 12:14 |
||||
Tags: Dev Diary |
||||
|
||||
So after having spoken to a friend the other day and trying to get him to start using XMPP (aka as Jabber) instead of facebook messenger I realised that even while I thought it was trivial to set up other people might disagree with me. So here a little guide :) |
||||
|
||||
Now...this isn't just a step-by-step instruction of what to do. In fact this article is more about getting you to understand XMPP than registered with a specific server. XMPP (formerly known as Jabber, just so you know why some people use the terms interchangably) is a chat protocol, not a chat service. It is based on XML and was originally created for near instantanious message delivery (chats). However since then it has been extended to also be able to VoIP and video (more or less good, that all depends on your client). |
||||
So XMPP is a protocol that anyone can just use. However...when I say "get started with XMPP" I don't mean set up your own servers and chat system. I mean Jabber, which is still the name of a chat service. |
||||
|
||||
|
||||
### A network instead of a server |
||||
|
||||
The current open Jabber infrastructure is built around a bunch of servers (actually quite a **lot** of servers) that can communicate with each other. While inside the network it doesn't matter what server someone is registered with. As long as both people are part of the network they can chat with each other. And that's that. (Cryptographically and version wise it becomes a bit more complicated than that. But for the end user, that's what it boils down to). |
||||
|
||||
There is a list of servers in the Jabber network (also called directory) available [here](https://xmpp.net/directory.php). As you can see there are quite a few servers out there that will allow you to register. Now...when picking a server please make note of a few things. |
||||
|
||||
- Check the software has had updates in at least the last year. You don't want to trust your private chats to outdated software, especially because that will usually mean that the server admin can't be bothered to update to newer versions of plugins and protocols. |
||||
- Check that the server passes both "Server to Server" and "Client to Client" security tests. (Both in the green). |
||||
- You like the domain. You don't want to have a domain "@kinkymotherfucker.com" if you don't like it :D |
||||
|
||||
|
||||
### Register with a server |
||||
|
||||
So let's asume you've found a server you like. Scolling through the list I would probably register at [blah.im](https://blah.im). Note that you will have to import an SSL certificate. You will have to if you don't have the CA [Cert Root certificate installed](https://www.google.com/search?q=What+is+CA+Cert&ie=utf-8&oe=utf-8#q=What+is+CACert). |
||||
|
||||
But you will also notice that the server doesn't actually have anything on it's website. To register on servers in a lot of cases you will need a jabber client to do it for you. I am using Pidgin and will thus also demonstrate it with that client. Pidgin is free open source software and runs on almost any platform. But feel free to use a different client if you find one you like more. The features should all just be transferable. |
||||
|
||||
To install Pidgin please go to their website and follow download and installation instructions for your platform. For mine (Fedora) it's as simple as typing `dnf install pidgin`. I will assume you managed to install it and we |
||||
move on :) |
||||
|
||||
In Pidgin navigate to accounts and Manage your accounts. |
||||
|
||||
![Pidgin Manage Accounts](/images/jabber/pidgin1.png "Manage accounts") |
||||
|
||||
In the opening window click on **add** and then select XMPP from the list of possible accounts to add. |
||||
Fill in your desired username, the server you want to register with and a passphrase. You can leave the resource blank. Also make sure you tick the box "Create this new Account on the Server". |
||||
|
||||
Servers provide different ways to register. Some just have a registration webpage, some have an API that pidgin can talk to. Some make pidgin open a browser window and guide you to their registration site. This is something unique to the server you choose to register on. |
||||
|
||||
In the case of the *blah.im* server pidgin opens a new website where I can register my nickname (which I will not do because I already have an account I like to use). Check your input with the example picture below. |
||||
|
||||
![Creating new Account](/images/jabber/pidgin2.png "Create new account") |
||||
|
||||
And that's that. You should be registered and ready to log in and chat with other people who also use Jabber/ XMPP, no matter what server they're on. |
||||
|
||||
|
||||
### Encryption via Jabber (OTR) |
||||
|
||||
Jabber by itself can be secured via SSL and several transport layer security measures but that makes it no more secure than any other service. The server provider can still read all messages and log them without you ever knowing it. |
||||
|
||||
Because of that a lot of people use separate encryption with Jabber called "Off the Record", short "OTR". |
||||
|
||||
What OTR does is encrypt messages on your computer, sends them to your friend and then locally on their computer decrypts them again. This has however two drawbacks. |
||||
|
||||
1. Both you and your friend need to be online to chat with each other over OTR. You can't send them an offline message and let the server cache it until they come back online to read it. |
||||
2. OTR does not support multiple devices. That means you can't start chatting on your PC, have to leave and pick up the conversation on your phone. You will need to start a new conversation. And a lot of mobile clients don't properly support OTR because they shut down the session when you lock your screen. |
||||
|
||||
To address both these issues there is a new crypto protocol called "Axolotl" which fixes both of these issues. Axolotl is however a generic protocol and can be used with literally anything. To adapt it to XMPP and integrate it into the already existing infrastructure of servers there is a second protocol called "OMEMO" which implements Axolotl for XMPP. It is however still very new and *very* few clients support it at this time. In fact, the only Jabber client I know of is **Conversations** on Android. |
||||
|
||||
But let's assume the downsides of OTR don't bother you (they don't bother most people). How would you go about using it? OTR in Pidgin is a plugin that needs to be enabled. Depending on what platform you install it to you might have to install the plugin yourself which can be more or less work. (On Fedora it's just `dnf install pidgin-otr`) |
||||
|
||||
But [you can figure that out yourself, I hope.](http://lmgtfy.com/?q=Install+Pidgin+OTR+plugin). |
||||
|
||||
*Waits for you to install the plugin* |
||||
|
||||
Good, now what needs to happen. First you need to active the plugin and generate yourself a key. Go to **Tools** and then **Plugins**, search for the OTR plugin and enable it. Then go to it's configuration page. |
||||
|
||||
<img class="dual" src="/images/jabber/pidgin3.png" align="left"><img class="dual" src="/images/jabber/pidgin4.png" align="right"> |
||||
|
||||
You will need to generate a key. A key in this case means a private key, if you're already somewhat familiar with cryptographic concepts. It's a key that is unique to you, should be protected, private and *never* shared with anyone. It is thus called your **private key**. |
||||
|
||||
When clicking the button to generate a key Pidgin will make one for you and save it somewhere on your filesystem. It allows you to encrypt and decrypt data (chats, files, etc.) that people send to you. |
||||
Afterwards a key fingerprint will show up and the generate button will be greyed out. |
||||
|
||||
A key fingerprint is sort of a signature. It can identify you as you. So if someone sees your fingerprint they can be sure they're talking to the right person (if they've verified the fingerprint via another medium, e.g. meeting in person). But the fingerprint doesn't expose any secure information about your key. |
||||
|
||||
You can also change some basic information about how OTR should work on your system. I won't go over these for now. |
||||
![OTR Configuration](/images/jabber/pidgin5.png "OTR Config") |
||||
|
||||
And that's it. You're done. You can initiate new private conversations with people via the **OTR** submenu in the chat screen. And know that everything you say to the person in that session is secure. And here is the best thing: OTR provides something known as "forward secrecy". That means that if at some point someone steals your laptop or phone and you loose your private key that doesn't mean that, even if someone logged every single piece of encryted text going over the network between you and a certain person and has all the information needed to theoretically decrypt your messages, they can't! |
||||
|
||||
Because while you chat with OTR the key continously changes. So if you ever loose your key, you don't have to worry about old chats becoming public or visible for others to see. |
||||
|
||||
(As long as you don't log them in cleartext of course). |
||||
|
||||
![Let's go off the record](/images/jabber/pidgin6.png "Let's go OTR") |
||||
|
||||
#### Happy chattin' |
||||
|
||||
~ Kate |
@ -1,127 +0,0 @@ |
||||
Title: Hacking on Reedb |
||||
Category: OldBlog |
||||
Date: 2016-03-14 10:43 |
||||
Tags: Dev Diary, Reedb |
||||
|
||||
So...it's been a while :) Exams are over, code has been written, bugs have been fixed, frustrations have been had. Terrible christmas gifts have been sold off on ebay and found a new owner with a misguided sense of style. I've also gone a bit mad with one of my other projects: The Christmas bauble. As you might recall it started as a harmless joke to learn KiCAD, ended up actually being manufactured (I never saw that bonus from dirtypcb for mentioning them a lot :c) and has now gone into planning phase for Revision B. |
||||
|
||||
![Reedb Banner](/images/reedb_banner.png "Reedb Banner") |
||||
|
||||
But more on that later. I'm back, in the spring with new energy and drive. To talk to you about Reedb (yet again). |
||||
|
||||
With `0.12` coming closer and closer to being a reality I wanted to quickly draft up something how to interact with Reedb. The API is basically stable at this point and while the C-binding isn't quite done the C++ interface is ready to be used (and almost actually hooked up :) ). |
||||
|
||||
|
||||
### Initialising a context |
||||
|
||||
Getting started with reedb requires a context which holds a bunch of information about what vaults exist, what tokens haven been scoped, users active, watchdogs, etc. etc. |
||||
|
||||
In addition to that there are vault interfaces that get attached to a context that can then actually interact with vaults. This way different vaults can get handled by different interfaces that are completely separated from each other. |
||||
|
||||
```C++ |
||||
reedb *rdb = new reedb(); |
||||
rdb->set_os(LINUX); |
||||
rdb->set_distro(SYSTEM_D); |
||||
rdb->set_verbose(true); |
||||
rdb->finalise(); |
||||
``` |
||||
|
||||
The OS and sitro flags determine what configuration paths and formats are specified as well as how to launch reedb at system startup (if such a behaviour is wanted/ set up). |
||||
|
||||
After defining all the parameters required or wanted to initialise the Reedb context call finalise to make it official and make the context usable. Before `finalise()` is called, trying to access other functions via the context will result in an error being thrown. |
||||
|
||||
### Vault interfaces |
||||
|
||||
So after having a Reedb context you have to register a vaults interface to it. Multiple interfaces can be registered and separated which means that certain vaults can be accessed that require different settings (for example a minimum passphrase length). Generally it just offers more flexibility to the developers. |
||||
|
||||
```C++ |
||||
rdb_vaults *v = new rdb_vaults(); |
||||
rdb->register_vinterface(v); |
||||
|
||||
vault_meta meta = v->create("fancy_vault", "~/Documents/", "MyD0gisnot!mypassword!"); // P.S. I don't have a dog :) |
||||
``` |
||||
|
||||
The create function will generate a key, encrypt the Master key with the provided passphrase and dump it to disk. In addition a folder structure and configuration is written. The config is mostly future proofing - none of the values are actually currently used. But it will hold information about zones, users and cipher modes in the future. |
||||
|
||||
--- |
||||
|
||||
After creating a vault you still need to authenticate on it. The unencrypted key might still be held in RAM (in secure memory that is) but just because you created a vault doesn't automaticaly mean you have access to it. So after calling `create` you need to call: |
||||
|
||||
```C++ |
||||
// A token is malloced for you in secmem. Do not free it yourself. Let Reedb do it for you! |
||||
rdb_token *token = v->authenticate(meta.id, "MyD0gisnot!mypassword!"); |
||||
``` |
||||
|
||||
You need the UUID from the interface we are addressing the vault via - we can find the UUID in the vault_meta we were handed during creation. Alternatively we can ask the vaults interface. |
||||
|
||||
From the docs: |
||||
> A UUID is provided from the management wrapper and isn't stored in the vault itself. A vault doesn't care about its own ID, nor does it even know it has one. |
||||
> |
||||
> Do not try to hard-code UUIDs into your program as they might be non-persistent across runtimes. |
||||
|
||||
Authentication only takes the ID and passphrase at the moment. However a user-auth function will be added in at least the next version. Both return a token that will be required for **every** operation that follows. |
||||
|
||||
And that's it...you can now interact with your shiny new Reedb Vault :) |
||||
|
||||
```C++ |
||||
std::string file_id = "Reedb.org"; |
||||
map<std::string*, std::string*> content(); |
||||
content["Username"] = "Peter Pan"; |
||||
content["Passphrase"] = "flower123"; |
||||
|
||||
/* Then take all that data and insert it */ |
||||
v->insert(meta.id, token, &file_id, &content); // Takes the pointer to a content map to save memory during inserts. |
||||
``` |
||||
|
||||
As you can see you need a vault-id and a token to even be allowed to the next step. Then to insert a piece of data you need to give it a name. Reedb is object-oriented which means that every dataset has a name and is an "object" on the FS ( Blockdevice mode is in planning :) ). So from that day on your piece of data will be available if you query for "Reedb.org". |
||||
|
||||
```C++ |
||||
map<std::string*, file_meta*> data; |
||||
data = query_file(meta.id, token, "Reedb.org"); |
||||
``` |
||||
|
||||
That will put a query return into your map. A query return isn't quite data. It's basically a name of a data-set mapped to it's head. A file head contains a bunch of metadata that isn't exactly deemed "important". Like it's name, a category, some tags and whatever else you might want to save in there. |
||||
In fact you can extend header fields at will. |
||||
|
||||
```C++ |
||||
map<std::string*, std::string*> meta_delta(); |
||||
// ... |
||||
v->migrate_headers(&meta_delta); |
||||
``` |
||||
|
||||
From the docs (again): |
||||
|
||||
> A meta_delta is the name of a meta-field that should be inserted mapped to its type in a std::map<?,?>. |
||||
> If a meta should be removed set the type to "-1". |
||||
> |
||||
> When removing meta fields from active vaults data needs to be migrated via rdb_meta_migr(...). Also be aware that removing active meta fields can cause terrible memory corruption. Be warned! |
||||
|
||||
A file_meta is exactly that: a vault header. It can be further searched and filtered with RQL (Reedb Query Language) that we will not go into further in this blog post. Just know that it exists :) |
||||
|
||||
*hint hint* `"$CATEGORY: [Social | Website | Online] $TAGS:[Private & Friends] $NAME: ~[Face]"` :) |
||||
|
||||
Deleting, updating files and updating vaults is analogue to what we already saw. Basically you always keep your vault ID and token on you, then provide the interface with some data. |
||||
|
||||
Some of the steps might seem a bit verbose but that's just so that the user (aka developer) gets maximum control over what she is doing with her code. It also allows for more precice error handling - narrowing down the source of the error further for the end-user. |
||||
|
||||
### A tiny last thing |
||||
|
||||
There are two interfaces for Reedb. A C++ and a C one. And you pick which one you want to use by either doing |
||||
|
||||
```C++ |
||||
#include<reedb/core.hpp> |
||||
``` |
||||
|
||||
or |
||||
```C |
||||
#include<reedb/core.h> |
||||
``` |
||||
|
||||
The C Interface is pretty much analogue to the C++ one (with obvious slight differences). |
||||
|
||||
```C |
||||
vault_meta *meta; |
||||
rdb_vaults *vaults = rdb->create(&meta, ...); |
||||
``` |
||||
|
||||
That's it for today. I hope this article gave you a quick introduction to the native interface and makes you at least a little curious or excited to work with it :) |
@ -1,116 +0,0 @@ |
||||
Title: LibGDX UI utility: Super UI |
||||
Category: OldBlog |
||||
Tags: Dev Diary, LibGDX, Game Dev, Lonely Robot |
||||
Date: 2017-01-24 00:14 |
||||
|
||||
**Let me tell you a factual statement** |
||||
|
||||
*UI programming is terrible* |
||||
|
||||
**Let me tell you an even more factual statement** |
||||
|
||||
*UI programming in LibGDX is even more terrible* |
||||
|
||||
I am a big fan of LibGDX. It's a really nifty library/ framework to get started with game development if you're more comfortable inside a code editor than a full blown game engine that is more targeted towards designers and artists. And I put my money where my mouth is: I have a series about LibGDX development for beginners on this blog and work almost exclusively with it when it comes to my own projects. |
||||
|
||||
Yet, there is something that bothers me and there didn't seem to be a great solution to fix it. UI code structure. In this post I want to highlight a utility I have written for LibGDX which is very easily embeddable into your existing projects which will you help structure UI code more efficiently. |
||||
|
||||
## The root problem |
||||
|
||||
The reason I dislike UI programming with LibGDX is that it usually results in very long code files or passing dozens of parameters into sub-classes that are needed to update the UI for button presses, etc. |
||||
|
||||
This goes so far that I have written an editor for game assets before just to realise that (once the development was complete) it had become completely unmaintainable and I had to start from scratch with better structure. It is incredibly easy to just throw out a UI design with Scene2D and LibGDX but unfortunately it is equally easy to produce very bad code which will turn into a big spaghetti mess. |
||||
|
||||
Let's look at an example problem that I wanted to solve. |
||||
|
||||
![LibGDX UI design problem](/images/libgdx_ui/01_base_problem.png) |
||||
|
||||
Looking at this structure we have three main components that interact with each other. We have a class that handles UI logic (setting up actors in tables, adding listeners, etc), we have a window state which in the particular case which made me write an alternative was a "Lobby Handle" which coordinated what players were going to enter a match, the map, game mode and if everybody in the multiplayer match was set to "Ready". Lastly we have the actual network signal handlers that listen to TCP/ UDP packets and execute code to write/ read from the window state as well as update UI elements. |
||||
|
||||
Implementing this structure with Scene2D and LibGDX will result in a lot of very ugly code. Because the network signals need to know everything about the UI (how it is structured, etc). And our window state can be written to by two different sources which means that we need to mutex it to avoid race conditions. |
||||
|
||||
So, what was I trying to solve? First a bit of limitation of scope. Because a lot of UI problems have been solved over and over again and usually at the cost of runtime performance or with a *lot* of extra code. |
||||
|
||||
1. UI code doesn't have to be embedded in a screen |
||||
2. All UI code can access the shared context of the screen |
||||
3. UI elements can update each other |
||||
4. Clean API that can be called on from anywhere (with a reference to the handle) that triggers range of functions. |
||||
|
||||
So with that in mind, this is what I did. |
||||
|
||||
```java |
||||
|
||||
class MyUIHandle extands UIHandle { |
||||
public static enum UI implements UI_BASE { |
||||
PLAYER_LIST; |
||||
} |
||||
|
||||
{ /** Initialiser block for new objects */ |
||||
|
||||
registerHandle(new PlayerList(), UI.PLAYER_LIST); |
||||
// ... more handles |
||||
} |
||||
|
||||
@Override |
||||
public void initialise(Stage s, Object ... var) { ... } |
||||
|
||||
public class PlayerList extends UIContainer { |
||||
|
||||
@Override |
||||
public void initialise(Stage s) { ... } |
||||
|
||||
// Define more API here ... |
||||
} |
||||
|
||||
} |
||||
|
||||
``` |
||||
|
||||
When we initialise a new `UIHandle` the initialiser block will create our `PlayerLists` and register them with the `UIHandle`. That code is hidden away from you. You can see that we're implementing a different enum type that we overload with values so that we can address submodules via a compile-time checkable value (such as enums). From inside (and outside) this class `UIContainer's` are available via `handle.get(UI.SUB_HANDLE)`. Obviously keeping your enum labels short will make your function calls snappier :) |
||||
|
||||
The following graphic will sort-of explain the layout in more detail. |
||||
|
||||
![Super UI fixing attempt](/images/libgdx_ui/02_ui_structure.png) |
||||
|
||||
What you might also notice is that the `UIHandle` has an initialise function with variadic parameters while the `UIContainer` class only takes a stage. That is because window context is stored once in the `UIHandle` and then accessable from all `UIContainer` classes. This way we only need to do the inversion of control pattern once instead of for every sub-component. |
||||
|
||||
You can keep the `UIContainer` classes outside this code-file. Then you might however want to provide a construct that does another inversion of control so that an external `UIContainer` can access the context provided via initialise! |
||||
|
||||
```java |
||||
|
||||
public class PlayerList extends UIContainer { |
||||
|
||||
private MyUIHandle parent; |
||||
public PlayerList(MyUIHandle parent) { this.parent = parent; } |
||||
|
||||
// ... |
||||
} |
||||
|
||||
``` |
||||
|
||||
Now let's talk about that public API. In our original example we wanted to have networking code update some UI elements. And we want UI elements to update other UI elements. So first of all, we keep context in each `UIContainer` about what UI elements are accessable to it. So what we can do in every of our submodules is this: |
||||
|
||||
```java |
||||
parent.get(UI.PLAYER_LIST).updatePlayers(playerList); |
||||
``` |
||||
|
||||
It also means that if we get new data from – say – a network socket or AI simulation, we can very easily update data in some random UI element. |
||||
|
||||
```java |
||||
handle.get(UI.PLAYER_LIST).populate(playerList); |
||||
``` |
||||
|
||||
So all in all, we have solved the following problems: |
||||
|
||||
1. We have access to all game state in the UI code without passing too many parameters into lots of sub-classes |
||||
2. UI code can be moved into lots of files for easier understandability |
||||
3. Context isn't duplicated |
||||
4. UI code can update other UI code without needing a direct reference to it. |
||||
|
||||
The individual `UIContainer` instances are essentially independant of each other via dependency injection. |
||||
|
||||
This library isn't done yet. Most of this is kinda hacked together to fit into **my** game. But I'm interested in making it more generic and putting it on Github. Especially because I can see myself using it again in the future. |
||||
|
||||
Hope this might be useful to somebody out there. If you have questions, comments, hatemail... |
||||
|
||||
[Twitter](https://twitter.com/spacekookie) or [E-Mail](mailto:kookie@spacekookie.de) |
@ -1,60 +0,0 @@ |
||||
Title: Dabbling with Moonscript |
||||
Category: OldBlog |
||||
Tags: Dev Diary, moonscript, programming |
||||
Date: 2017-05-06 11:55 |
||||
|
||||
![Lua means moon in portuguese](/images/lua_moon_banner.png) |
||||
|
||||
Recently I've started learning/ using Moonscript. It's a language that compiles to [lua](https://www.lua.org/) and as such can run in the LuaJIT, an alternative lua engine which allows very easy and *fast* ffi calls into native code. This makes lua code capable of writing very performant applications and games that use native rendering, window creation or general libraries. |
||||
|
||||
But in my opinion lua has always felt a bit cumbersome. I use awesomewm so I had to write it occasionally to customise my UI layout. And this is where Moonscript comes in. It's a lot of syntactic sugar on top of lua as well as some other concepts such as object orientation which lua just plain out doesn't have. And while yes, you can write good code without OO (*cough* **C** *cough*) it is a nice tool to have in your pocket, especially when writing GUI applications or games. |
||||
|
||||
|
||||
## The language |
||||
|
||||
```Moonscript |
||||
class Thing |
||||
name: "unknown" |
||||
|
||||
class Person extends Thing |
||||
say_name: => print "Hello, I am #{@name}!" |
||||
|
||||
with Person! |
||||
.name = "MoonScript" |
||||
\say_name! |
||||
``` |
||||
|
||||
As you can see Moonscript is an indentation based language which (in my opinion) combines syntactic elements from lua and ruby together. In the snippet above (which is from the [moonscript website](http://moonscript.org/)) you can see classes, inheritance as well as the `with` keyword which allows you to initialise/ work with objects without typing it's variable name over and over again. |
||||
|
||||
If you want to learn more about the language, I can only recommend you have a look at the [Moonscript in 15 minutes guide](https://github.com/leafo/moonscript/wiki/Learn-MoonScript-in-15-Minutes) |
||||
|
||||
|
||||
## How to use it |
||||
|
||||
You can just write Moonscript files, add `#!/usr/bin/env moon` to them and get going. Obviously that's pretty cool for little scripts that you just want to get going. But not so great for larger applications because a) you don't have access to `ffi` via luaJIT and b) it adds additional startup cost. |
||||
|
||||
So instead for my projects so far (which so far are a [game](https://github.com/spacekookie/dinodino) and a desktop app) I use a `Makefile` to build and run the Moonscript compiler and then execute the `init.lua` with luajit. |
||||
|
||||
```Makefile |
||||
SOURCES := $(wildcard *.moon) $(wildcard **/*.moon) |
||||
LUAOUT := $(SOURCES:.moon=.lua)0 |
||||
|
||||
.PHONY: all run build |
||||
|
||||
all: run |
||||
build: $(LUAOUT) |
||||
%.lua: %.moon |
||||
moonc $< |
||||
run: build |
||||
luajit init.lua |
||||
``` |
||||
|
||||
## Wrapping up |
||||
|
||||
So...I'm kinda excited about this. Most of the code I write is either in C or Java (depending on what exactly I'm doing). And those two strongly typed and compiled languages have served me well and will continue to be my go-to solutions for a lot of problems. |
||||
|
||||
But I've long been looking for a dynamicly typed, interpreted/ just-in-time compiled language that I can use for anything from little scripts to medium-sized desktop applications. I used to use python for this but have recently (over the last 6-9 months) fallen out of love and developed a rather passionate dislike of it and it's ecosystem. |
||||
|
||||
My current project will get it's own little article at some point but I don't mind teasing the progress here. I'm writing a new UI for redshift which works with X11 linux backends and is heavily inspired by f.lux on MacOS. It's written in moonscript, with my own forked version of redshift (which I call [libredshift](https://github.com/spacekookie/libredshift)). It's on [github](https://github.com/spacekookie/redshift_ctrl) and licensed under MIT. |
||||
|
||||
Hope I've made you a little curious about Moonscript. And stay tuned for updates on future projects with it :) |
@ -1,112 +0,0 @@ |
||||
-----BEGIN PGP SIGNED MESSAGE----- |
||||
Hash: SHA256 |
||||
|
||||
❤ (rayya) ~> cat msg |
||||
Following are my public gpg key as well as Threema Fingerprint and others. |
||||
|
||||
Threema |
||||
======= |
||||
ID: 77WYDHA2 |
||||
Fingerprint: AB2A 4F8A 8FF9 6335 75C2 4E0C 175B C0D6 |
||||
|
||||
Public Key Cryptograpy (GnuPG) |
||||
============================== |
||||
|
||||
Plain copy of my key can be found here |
||||
https://spacekookie.de/pgp/6FE1BBF3.asc |
||||
|
||||
It is also available on the MIT keyserver. |
||||
|
||||
This document was last changed/ signed at around |
||||
❤ (rayya) ~> date |
||||
Sun 7 May 14:09:59 CEST 2017 |
||||
❤ (rayya) ~> gpg2 --fingerprint 6FE1BBF3 |
||||
pub rsa4096 2017-02-02 [SC] [expires: 2019-02-02] |
||||
9F18 A093 CF65 F938 E4C8 EFA4 29E0 5751 6FE1 BBF3 |
||||
uid [ultimate] spacekookie c-base <spacekookie@c-base.org> |
||||
uid [ultimate] spacekookie (Using computers to create weird random patterns...) <kookie@spacekookie.de> |
||||
sub rsa4096 2017-02-02 [E] [expires: 2019-02-02] |
||||
|
||||
❤ (rayya) ~> gpg2 --armor --export 6FE1BBF3 |
||||
- -----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
|
||||
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 |
||||
=vSgE |
||||
- -----END PGP PUBLIC KEY BLOCK----- |
||||
-----BEGIN PGP SIGNATURE----- |
||||
|
||||
iQIzBAEBCAAdFiEEnxigk89l+TjkyO+kKeBXUW/hu/MFAlkPFaQACgkQKeBXUW/h |
||||
u/NbgQ/+LRikzv8yFpVzbNZTg1mEQ+wFDZEnrwSDvdEL/+iqZ8X8V3NWz9svUIF/ |
||||
mMjQeYH3XcZqJ19XpyfSd+1MiFoNRCU04aaOwopmWrok0qYoKW7zdwTHmiBnftCs |
||||
vctvO5o0INN2Qfrhwjzot2EfBiuo6LBG3lV6rWdMZFkJFlzoCqlFrMVWBAYBtXlN |
||||
QhPgxfanp3Fg7hKGS5sYjtGlGEY6Qu1nkbU/92sHtCnSb+2TM8fRij4Su/5B+XMH |
||||
e2UbmMcSY2iS2tn46sG0hAVky5+BF2JHKWVOECrszqVwxDRhqPBdVOKD3biXrQsM |
||||
9NistBGaFoYcVxN7wvhVdOd07pqc6HV1Dmv3ngro6kX3j9KsB/70OvKntsVWbnph |
||||
kEwoOVAhPs8f6zNo/lGhs9vYsLgkbSpzflKYlEqHlOFOPSGzHAAeM7eMi6aWVMWB |
||||
LnVxaKtogE1m7lPdgkHt0V6sqHUFf+j7eHrWp0J8w514FcO9dhhNiY0Yvn1xJCBM |
||||
tEkemLyMfk8gx3M1lz8p7RkKR601GcXi6mtpetREPL40zUt8IhRDwUwXM6Pvuspl |
||||
pKUxhkbE8CeJ9vvyqVhoAjTfEGDpXI6sb53le7RXkw8JdQ9ueoJyt4Fk/zXXxvdC |
||||
3XMB6dIEFQ979IHp04Pb8njdWEokzNOMlUPyrTR0UvYXUPeDmhs= |
||||
=ZvDg |
||||
-----END PGP SIGNATURE----- |
@ -1,114 +0,0 @@ |
||||
-----BEGIN PGP SIGNED MESSAGE----- |
||||
Hash: SHA256 |
||||
|
||||
❤ (idenna) ~/pgp> cat msg |
||||
Following are my public gpg key as well as Threema Fingerprint and others. |
||||
|
||||
Threema |
||||
======= |
||||
ID: 77WYDHA2 |
||||
Fingerprint: AB2A 4F8A 8FF9 6335 75C2 4E0C 175B C0D6 |
||||
|
||||
Public Key Cryptograpy (GnuPG) |
||||
============================== |
||||
|
||||
Plain copy of my key can be found here |
||||
https://spacekookie.de/pgp/6FE1BBF3.asc |
||||
|
||||
It is also available on the MIT keyserver. |
||||
|
||||
This document was last changed/ signed at around |
||||
|
||||
❤ (idenna) ~/pgp> date |
||||
Thu 2 Feb 11:52:00 CET 2017 |
||||
|
||||
❤ (idenna) ~/pgp> gpg2 --fingerprint 6FE1BBF3 |
||||
pub rsa4096 2017-02-02 [SC] [expires: 2019-02-02] |
||||
9F18 A093 CF65 F938 E4C8 EFA4 29E0 5751 6FE1 BBF3 |
||||
uid [ultimate] spacekookie c-base <spacekookie@c-base.org> |
||||
uid [ultimate] spacekookie (Using computers to create weird random patterns...) <kookie@spacekookie.de> |
||||
sub rsa4096 2017-02-02 [E] [expires: 2019-02-02] |
||||
|
||||
❤ (idenna) ~/pgp> gpg2 --armor --export 6FE1BBF3 |
||||
- -----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
|
||||
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 |
||||
=vSgE |
||||
- -----END PGP PUBLIC KEY BLOCK----- |
||||
-----BEGIN PGP SIGNATURE----- |
||||
|
||||
iQIzBAEBCAAdFiEEnxigk89l+TjkyO+kKeBXUW/hu/MFAliTEe0ACgkQKeBXUW/h |
||||
u/Pb1w/+IYx8RUaxGQxq8jphBaCwAfUvOOl3DQwPLETsvz1UYQPcNTmXbKK8GziJ |
||||
ki69eizxMcikVwTT0CinRX0NtE1qeGKmPiTT9Zxoi688QeXdDZDksOrLHGS+bqfV |
||||
WxwQG2hGUjLUqCjKsBIz4OwiZ8xqff+Z3HqYhTc1GpdwbIXG43l+tLFkU5eTaERU |
||||
u/uSPjnmbxrZRGLzv0fnrppEKXPTBmm8O3hIM9Cp+rZIQw9rbbkGSGeRXtSDDsC/ |
||||
6dkBKvM7YDqw/9/EnFbrw1vMBnVDdmMvMH/KQIxDuF+mEkq9eNOvR8QCJa8SDMOH |
||||
Hdhi8sJqS7oX91kH0TU86Uym+/GKlUfq/VDZCGssOqPlFLc0ZOWKL1VoReVwb+34 |
||||
hkowMP5i4XieXuY4JtBTuDjvGbauZDorFqONosAzq67zf2FTY3JrGZay0Q+3/rs9 |
||||
q3FbU8esjM/q64XltuWmZfGnbRU3Xdfr+V/YCqLiUXfsLJ38WZBXruL31ar9CjcL |
||||
PMN/YipyvSkSze8IBHBtyonDg5Zug7ntL6Ku58XitQBHRy8KDuJL74RiABmri2DM |
||||
Tpo1kwGokd3Sj24VX4JEg/jAczC93x/F9B2BKCY1tvqSDCdavrToSbSOva7//9sC |
||||
MV2oN8slEE5fPgxT9A8DTORImfstI9AnUBUn+julhMer3FSaNWk= |
||||
=MXQM |
||||
-----END PGP SIGNATURE----- |
@ -1,16 +0,0 @@ |
||||
Following are my public gpg key as well as Threema Fingerprint and others. |
||||
|
||||
Threema |
||||
======= |
||||
ID: 77WYDHA2 |
||||
Fingerprint: AB2A 4F8A 8FF9 6335 75C2 4E0C 175B C0D6 |
||||
|
||||
Public Key Cryptograpy (GnuPG) |
||||
============================== |
||||
|
||||
Plain copy of my key can be found here |
||||
https://spacekookie.de/pgp/6FE1BBF3.asc |
||||
|
||||
It is also available on the MIT keyserver. |
||||
|
||||
This document was last changed/ signed at around |
@ -1,270 +0,0 @@ |
||||
Title: 02. (LibGDX) Game of Codes: Input & Movement |
||||
Category: Game of Codes |
||||
Tags: LibGDX, Tutorial, Game Dev |
||||
Slug: 02-input-and-movement |
||||
Status: published |
||||
|
||||
Welcome back to the Game of Codes, an introduction series to the LibGDX framework. In the last edition we learned how to set up LibGDX with a new Java project and draw simple pictures onto the screen. We used textures to import that image and then drew it via a SpriteBatch. |
||||
|
||||
Today we will look at basic input handling and how to make things move on screen. And though we won't be able to cover everything in this article we will explore the basic input stack that LibGDX has to offer and how to make things in your game move. |
||||
|
||||
Exciting! :) |
||||
|
||||
A little note: all the code that gets shown off here is available in a [Github repository](https://github.com/spacekookie/starchaser) for you to tinker with. After each tutorial I tag the commit so that it's obvious what got changed when! |
||||
|
||||
You can also use that repository to report issues or give feedback if you'd like. Otherwise, my email is always available! |
||||
|
||||
### Registering input |
||||
|
||||
Before we talk about inputs, we need to think about what it even means to register an input. When the user presses a button in our game, we want that button press to notify us so we can affect some behaviour. To understand what is going on here, we should consult the following graphic. |
||||
|
||||
![Life of a Frame](/images/gameofcodes/series02/01_framelife.png) |
||||
|
||||
You can see that LibGDX (obviously) considers the main run loop of our game...a loop :) In this series we only really care about the purple boxes. And in this article in particular, we are only considering the first purple box: "Input". What LibGDX does during this step is poll all input hardware for activity. It then writes this activity into a buffer and signals all registered input adapters to handle their input. |
||||
|
||||
So with that in mind, there are two ways of checking for input. The first is essentially polling the hardware again yourself during the "Render" step, while the other hooks into the "Input" step and is called asynchronously. |
||||
|
||||
Both ways of handling input are slightly unique. And we will start with the polling aproach first to demonstrate some basic functions. |
||||
|
||||
First, go into the main game class and add a position variable into the class body: |
||||
|
||||
```java |
||||
|
||||
import com.badlogic.gdx.math.Vector2; |
||||
|
||||
public class StarChaser extends ApplicationAdapter { |
||||
//... |
||||
|
||||
Vector2 position; |
||||
|
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
Furthermore, in the `create` block of the game, initialise the position to some value that is greater than `(0,0)` and not too big to be off window :) |
||||
|
||||
```java |
||||
position = new Vector2(250, 150); |
||||
``` |
||||
|
||||
If you're not too familar with Java, what this means is that we declare `position` to be a object variable which means that every function in an instance of this class can access it (Object-Oriented Programming). In the `create` function we then initialise it to have a value other than `null`. |
||||
|
||||
What that means now is that we can use the position variable (which has an `x` and a `y` component) in our draw calls to tell the picture where to go. The main advantage of this is that when we change the position variable (say...via a button press), the picture gets an updated position! |
||||
|
||||
```java |
||||
public void render() { |
||||
Gdx.gl.glClearColor(1, 0, 0, 1); |
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); |
||||
|
||||
updateInputs(); |
||||
|
||||
batch.begin(); |
||||
batch.draw(background, 0, 0); |
||||
batch.draw(img, position.x, position.y); |
||||
batch.end(); |
||||
} |
||||
``` |
||||
|
||||
So far so good...wait. Do you see that `updateInputs()` function call there? That wasn't there last time. In fact, it doesn't yet exist. But it should soon. Why don't you go and create a new function in the class somewhere with the signature `void updateInputs() { ... }`. We will fill it's body with some stuff soon. |
||||
|
||||
So fundamentally, we want to poll inputs and then when we have determined that an input is pressed we want to enact some functionality. The simplest form of polling buttons is with the `Gdx.input.isKeyPressed(key)` function where `key` is an integer variable that corresponds to a key-code. Don't worry, there are bindings that make it easier and you don't have to manually check for numbers ;) |
||||
|
||||
> **Tip** |
||||
|
||||
> Other input polling functions include `isButtonPressed(button)` for mouse buttons as well as `getX()`, `getY()` which give you the cursor position in your game window! |
||||
|
||||
|
||||
So why don't you add the following lines of code to your `updateInputs` function and see where it gets us. |
||||
|
||||
```java |
||||
if (Gdx.input.isKeyPressed(Keys.W)) { |
||||
position.y++; |
||||
} else if (Gdx.input.isKeyPressed(Keys.S)) { |
||||
position.y--; |
||||
} else if (Gdx.input.isKeyPressed(Keys.A)) { |
||||
position.x--; |
||||
} else if (Gdx.input.isKeyPressed(Keys.D)) { |
||||
position.x++; |
||||
} |
||||
``` |
||||
|
||||
You can run this now and see what happens. When we press the keys in question the image on screen will move all over the place. Cool! But...it's not particularly pretty, is it? |
||||
|
||||
For one, we can only move in one direction simultaniously. But even if we split the X-Y axis into two different if-blocks, there is still the problem that `W` will always have precedence over `S` and `A` will have precedence over `D`. Which means that if we press all keys, we will *always* move top-left. And that's not particularly great :( |
||||
|
||||
> **Tip** |
||||
|
||||
> Also consider the following: when you move in one direction you apply 1 to the axis you're moving along. But if you move in two directions, you apply 1 in both x and y direction. Which means that (via trigonometry) you actually move **~1.41** in total. This means your game isn't consistent about rules. |
||||
|
||||
> It's clear that more logic is required to move! |
||||
|
||||
So how do we fix this? We can of course add more logic to our `updateInputs()` function but it will result in a lot of dirty hacks. And while game development is often about making dirty hacks that work, starting a project off some will quickly make your code-base unmaintainable. |
||||
|
||||
|
||||
### Using Input Adapters |
||||
|
||||
The second method of getting input from the user I mentioned earlier is via an input adapter. It can be considered faster because we only do input polling once and it allows us to use input signals between different game components (gameplay, game HUD, etc.) |
||||
|
||||
So how do we use this awesome functionality? Well, it's simple. We need an InputAdapter. So first, create a new class via Eclipse. If you don't know how, consult the *suuuuper* helpful screenshot below :') |
||||
|
||||
![Life of a Frame](/images/gameofcodes/series02/02_createclass.png) |
||||
|
||||
Give it a useful name like `InputHandle` or `ShipInputHandle` or something. You can be quite specific in the naming because you very often have multiple input adapters for different aspects and parts of your game. So being specific in the naming just helps you out in the long run. |
||||
|
||||
Once you've done that you should be greeted with a very boring and empty class in your editor. So we need to add some basic code to get going. I took the liberty of doing that and will now show off what I did (and you'll finally get to see what name I chose...). |
||||
|
||||
```java |
||||
|
||||
import com.badlogic.gdx.InputAdapter; |
||||
import com.badlogic.gdx.math.Vector2; |
||||
|
||||
public class ShipInputHandle extends InputAdapter { |
||||
Vector2 shipPosition; |
||||
|
||||
public ShipInputHandle(Vector2 shipPosition) { |
||||
this.shipPosition = shipPosition; |
||||
} |
||||
} |
||||
|
||||
``` |
||||
|
||||
So, as you can see we have a class that extends `InputAdapter` as a subclass. With that comes free functionality we don't have to implement ourselves. Additionally I create a constructor that takes a vector and stores it as an instance variable (like before in the game class). Note that we're not copying the value here but rather storing a reference to the "original" variable in the game. |
||||
|
||||
> **Tip** |
||||
|
||||
> If you're coming from a language like C or C++ this can be quite confusing. What is a copy, what is a reference? In general: java always passes by reference (pointer) unless it is a primitive value. What is a primitive value? `int`, `float`, `double`, `boolean`, `byte`, `long` and all other lower-case types that become purple in the IDE (keywords). |
||||
|
||||
Next up, let's handle some inputs! The principle is similar to the polling: we check what input we are handling (because we only have generic functions - this will become obvious in a second), then invoke some behaviour. But as we have already seen before, we need to store some state. And that's why this is perfect: we have a new class where we can store the input state to check against. But at the same time, it's contained and doesn't clutter our main game class. |
||||
|
||||
Now...to solve the problem of moving in multiple directions at the same time, without letting one direction take precidence over another we can use a tri-state variable. In Java this can easily be done with an enum. Create a enum titled `TS` (or TriState if you feel verbose) in our `ShipInputHandle` class and create two values `x` and `y` that use it. The initial value should be `NEUTRAL` |
||||
|
||||
```java |
||||
enum TS { POS, NEG, NEUT }; |
||||
|
||||
TS x = TS.NEUT, y = TS.NEUT; |
||||
``` |
||||
|
||||
In the `keyDown(int keycode)` and `keyUp(int keycode)` functions we can then use a switch statement to flick the `x` and `y` variables in their favour. We *can* also perform a simple check if the `x`, `y` variables are already set to avoid another direction overwriting our current movement. But then again, maybe you consider this preferred behaviour. I chose to perform the check in the following code! |
||||
|
||||
```java |
||||
public boolean keyDown(int keycode) { |
||||
|
||||
switch (keycode) { |
||||
case Keys.W: |
||||
if (y == TS.NEUT) |
||||
y = TS.POS; |
||||
break; |
||||
case Keys.S: |
||||
if (y == TS.NEUT) |
||||
y = TS.NEG; |
||||
break; |
||||
case Keys.A: |
||||
if (x == TS.NEUT) |
||||
x = TS.NEG; |
||||
break; |
||||
case Keys.D: |
||||
if (x == TS.NEUT) |
||||
x = TS.POS; |
||||
break; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
``` |
||||
|
||||
Notice that `return true` at the end of that function? That's what you could call "Input Cascade". It is a concept that we will use extensively in later articles of this series. In short, it is the concept of letting an input signal cascade through different input adapters until it is ended. Returning true in this function signals the core input controller that we are ending the signal: it will not cascade to lower ranking controllers. This means that if you replace it with a `return false`, controllers down the stack will be able to pick up on the signal and use it. |
||||
|
||||
But again, this will become important in later tutorials. For now, let's just end the signal and get it over with. Next up, we can implement the `keyUp` function very simply by checking what axis our key-presses affect and then resetting that direction back to `NEUT` if it is applicable. Not a perfect solution but something that will definately work is implemented below. |
||||
|
||||
```java |
||||
public boolean keyUp(int keycode) { |
||||
switch (keycode) { |
||||
case Keys.W: |
||||
if (y == TS.POS) |
||||
y = TS.NEUT; |
||||
break; |
||||
|
||||
case Keys.S: |
||||
if (y == TS.NEG) |
||||
y = TS.NEUT; |
||||
break; |
||||
|
||||
case Keys.A: |
||||
if (x == TS.NEG) |
||||
x = TS.NEUT; |
||||
break; |
||||
|
||||
case Keys.D: |
||||
if (x == TS.POS) |
||||
x = TS.NEUT; |
||||
break; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
``` |
||||
|
||||
Now we're almost done. One thing is missing however! We keep a state depending on the inputs of our user. But we don't apply anything to the vector we stored based on that state. This is where we will need to build something slightly custom because the InputAdapter doesn't force you into any workflow. |
||||
|
||||
I recommend you create a new function `void update() { ... }` in the input class and make it public. We consider this function to be called every frame and apply values to the x and y components of the position vector, depending on the state of our inputs. |
||||
|
||||
The following code very quickly checks if we need to apply movement at all (is not NEUT) and then does a conditional application of 1 or -1 to each component. |
||||
|
||||
```java |
||||
public void update() { |
||||
if(x != TS.NEUT) shipPosition.x += (x == TS.POS) ? 1 : -1; |
||||
if(y != TS.NEUT) shipPosition.y += (y == TS.POS) ? 1 : -1; |
||||
} |
||||
``` |
||||
|
||||
Now we're done modifying the ShipInputHandle...for now :) Go back to the main game class. There are two more things to do before we can enjoy our new input handles. First, remove the old `handleInputs()` function. We don't need or want it anymore. Also make sure to remove it's function call from the `render()` function. |
||||
|
||||
Secondly, create a ShipInputHandle object and initialise it with our vector. Take the following code segment as reference. |
||||
|
||||
The last line in the `create()` function is key and not to be forgotten! It registers our custom input handler with the LibGDX input system and makes sure that our functions are *actually* being called :) |
||||
|
||||
```java |
||||
|
||||
public class StarChaser extends ApplicationAdapter { |
||||
|
||||
// ... |
||||
|
||||
ShipInputHandle input; |
||||
|
||||
@Override |
||||
public void create() { |
||||
// ... |
||||
|
||||
position = new Vector2(250, 150); |
||||
input = new ShipInputHandle(position); |
||||
|
||||
Gdx.input.setInputProcessor(input); |
||||
} |
||||
|
||||
@Override |
||||
public void render() { |
||||
|
||||
// ... |
||||
|
||||
input.update(); |
||||
|
||||
// ... |
||||
} |
||||
|
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
And that's it! Run that code and you'll be able to move the image around in a much nicer fashion! Again, this is far from perfect. And you will notice that switching quickly from going-left to going-right can make the whole thing just stop on "Neutral". You can remove the additional check which I added. Realistically, you need a lot more state to mirror what the user is putting into your system if you want real-feedback and logical behaviour from your units. But this will do for now! |
||||
|
||||
And more importantly...it should have given you a glimpse at how to use the InputAdapters. |
||||
|
||||
<hr /> |
||||
|
||||
And that's it for this article! Originally I wanted to talk a little bit about rotation. But I realised that I would have had to make a lot of assumptions about systems and not be able to go into too much depth without making the article *waaaayy* too long. |
||||
|
||||
So that'll be handled in the [next issue](https://media.giphy.com/media/z85AlA6CBKxEI/giphy.gif). |
||||
|
||||
Have a good day/ night, |
||||
|
||||
Kate |
@ -1,159 +0,0 @@ |
||||
Title: 03. (LibGDX) Game of Codes: Rotation & Advanced Movement |
||||
Category: Game of Codes |
||||
Tags: LibGDX, Tutorial, Game Dev |
||||
Slug: 03-rotation-and-advmovements |
||||
Status: published |
||||
|
||||
Welcome back to the Game of Codes, an introduction series to the LibGDX framework. In the öast edition we learned how to listen for user input, keep it's state for consistency and apply it to the world we are building. We did this by simply using a vector as a position for an image to align to. |
||||
|
||||
Today we will have a look at some more input but more importantly: rotation! And with that, also look at some more advanced movement concepts like momentum and some advice how to implement certain movement patterns. |
||||
|
||||
A little note: all the code that gets shown off here is available in a [Github repository](https://github.com/spacekookie/starchaser) for you to tinker with. After each tutorial I tag the commit so that it's obvious what got changed when! |
||||
|
||||
You can also use that repository to report issues or give feedback if you'd like. Otherwise, my email is always available! |
||||
|
||||
|
||||
### Naïve approach |
||||
|
||||
So let's just take a naïve approach here. We have a vector that is essentially the position of our "ship". And it can have a rotation. So what we do is listen for two new key presses (in my case for `Q` and `E` - left and right rotation) and then create another tri-state variable `rotation` that we can use to determine whether we should rotate left or right. |
||||
|
||||
```java |
||||
switch (keycode) { |
||||
case Keys.W: |
||||
// ... |
||||
|
||||
/** Handling rotation */ |
||||
case Keys.Q: |
||||
rotation = TS.NEG; |
||||
break; |
||||
case Keys.E: |
||||
rotation = TS.POS; |
||||
break; |
||||
} |
||||
``` |
||||
|
||||
The inverse applies for the `keyUp(...)` function. |
||||
|
||||
```java |
||||
switch (keycode) { |
||||
// ... |
||||
|
||||
case Keys.Q: |
||||
if (rotation == TS.NEG) |
||||
rotation = TS.NEUT; |
||||
break; |
||||
|
||||
case Keys.E: |
||||
if (rotation == TS.POS) |
||||
rotation = TS.NEUT; |
||||
break; |
||||
} |
||||
``` |
||||
|
||||
Our update code needs to be appended slightly. This isn't the most pretty way to do this but for now it'll be alright. |
||||
|
||||
```java |
||||
public void update() { |
||||
// ... |
||||
|
||||
if (rotation == TS.POS) |
||||
shipPosition.rotate(1); |
||||
else if (rotation == TS.NEG) |
||||
shipPosition.rotate(-1); |
||||
} |
||||
``` |
||||
|
||||
Now. What does this actually mean. We rotate the *positional* vector for the ship. You might have an inkling of what is about to happen but if you don't start the game and look at it. Not quite what we had in mind, is it? |
||||
|
||||
![Bad rotation GIF](/images/gameofcodes/series03/01_badrotation.gif) |
||||
|
||||
So what's happening here? Well, our positional vector points to where the ship is. From the origin. Which is in the bottom left of the screen. At coordinates `(0, 0)`... So when we rotate the positional vector, we rotate the ship around the origin. Furthermore, we never told the `SpriteBatch` to rotate the image we're drawing. That's why the ship orientation stays exactly the same: pointed upwards. |
||||
|
||||
|
||||
### Using TextureRegions |
||||
|
||||
So let's fix this one problem at a time. Let's actually make the ship texture rotate depending on some value (in our case, the phony vector angle). For this we need to look at how we draw things. Right now, that's a texture. |
||||
|
||||
A texture is essentially a raw memory map of an image, loaded onto the GPU. That's why the texture needs to be a power of two because that's how GPU's handle textures in their memory. But what this also means is that if we have multiple textures this will cause a lot of overhead because loading textures in and out of memory from the GPU is expensive. |
||||
|
||||
Also, all transformations to the texture we need to apply manually. Transformations include scaling, moving and rotation. And especially the last one can be challenging. |
||||
|
||||
**Enter: TextureRegion!** |
||||
|
||||
Now, a `TextureRegion` is a collection of textures, essentially a large texture with bits cut out of it. This way we can bundle all our textures together into one large one (or several large ones) while marking different parts of the texture as regions so that we can handle them in the future. |
||||
|
||||
What this also means is that simple transformations can be done on the CPU which is slower but much easier than performing them on the GPU. This means the textures are still stored on the GPU but we get more control over how to transform them. Let's use this in our game! |
||||
|
||||
```java |
||||
|
||||
public class StarChaser extends ApplicationAdapter { |
||||
|
||||
// ... |
||||
|
||||
TextureRegion img; |
||||
|
||||
// ... |
||||
|
||||
@Override |
||||
public void create() { |
||||
batch = new SpriteBatch(); |
||||
img = new TextureRegion(new Texture("artpack1/uss_pixel.png")); |
||||
|
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
The code above creates a TextureRegion instead of a Texture. In this case, we aren't using any of the memory saving benefits of using TextureRegions but that doesn't matter. We can still take advantage of it. |
||||
|
||||
Specifically, we will change the `batch.draw(...)` function to take more parameters. But first, create a second vector, call it "direction" and initialise it with `(0, 1)`. |
||||
|
||||
```java |
||||
|
||||
final float sizeX = img.getRegionWidth(); |
||||
final float sizeY = img.getRegionHeight(); |
||||
|
||||
batch.draw(img, // The TextureRegion we draw |
||||
position.x, // Root X position |
||||
position.y, // Root Y position |
||||
sizeX / 2, // Rotation origin X (center point) |
||||
sizeY / 2, // Rotation origin X (center point) |
||||
sizeX, // Draw width |
||||
sizeY, // Draw height |
||||
1, 1, // Scaling factor (1 is fine) |
||||
direction.angle()); // Region angle (around origin) |
||||
|
||||
``` |
||||
|
||||
Additionally, we need to adjust our ShipInputHandle class because we need to make it use our direction vector. The code snippet below will outline what you need to change. Essentially: we rotate our direction vector and noramlise it after every step because rotating vectors actually changes their length. |
||||
|
||||
```java |
||||
public class ShipInputHandle extends InputAdapter { |
||||
Vector2 pos, dir; |
||||
|
||||
// ... |
||||
|
||||
public ShipInputHandle(Vector2 shipPosition, Vector2 direction) { |
||||
this.pos = shipPosition; |
||||
this.dir = direction; |
||||
} |
||||
|
||||
// ... |
||||
|
||||
public void update() { |
||||
if (rotation == TS.POS) |
||||
dir.rotate(1); |
||||
else if (rotation == TS.NEG) |
||||
dir.rotate(-1); |
||||
|
||||
dir.nor(); |
||||
|
||||
// ... |
||||
} |
||||
} |
||||
``` |
||||
|
||||
If you launch this configuration you will notice that the ship rotates around it's centre point correctly! YAY! You will notice that you can still move your ship independant of it's rotation. You might consider this a feature because it allows you to fly one way and shoot backwards (think Battlestar Galactica Vipers!). But in our case, we want the ship to always fly in the direction that it's pointing towards. |
||||
|
||||
![Proper rotation](/images/gameofcodes/series03/02_rotating.gif) |
||||
|
||||
This is relatively simple. We only need to change the position update code to make this happen. In fact, we only need to consider the direction vector when applying a new position. |
@ -1,110 +0,0 @@ |
||||
Title: 01. (Java Native Access) The basics |
||||
Category: JNI |
||||
Tags: Java, Tutorial, Programming |
||||
Slug: 01-jni-the-idea-foreign-function-interfaces |
||||
Status: draft |
||||
|
||||
Regular readers of my blog (or listeners of my afk ramblings) will know that I like Java. I'm sometimes not quite sure why but there it goes, let's just assume that as an axiome for a second. |
||||
|
||||
Java is pretty damn good at a lot of stuff. It compiles to a high performant byte-code and it's JIT compiler is smart, fault-tolerant and self optimising. It has a plethora of libraries and toolkits to choose from and making nearly anything is pretty damn if you don't mind writing a verbose language or [cheating](https://projectlombok.org/) a bit. |
||||
|
||||
Then so far so good. What is this about then? Well...many things that Java doesn't do in it's standard lib you can still do. And it's something too many people to know about. For good reasons. Fault tolerance and optimisation on the side of the JIT compiler goes out the window and existing documentation is lackluster at best, in most cases outdated by decades at this point. Java is (among other things) an enterprise language and the tech hipsters have *looong* moved on from it. And so have people writing guides about it. |
||||
|
||||
That is about to change (to some small extent) |
||||
|
||||
|
||||
### What is JNI? |
||||
|
||||
The **J**ava **N**ative **I**nterface is a foreign-function-interface to the JVM, the virtual machine and JIT compiler environment that all Java code runs in. It allows you to write C/C++ code that is called by your existing Java project and even make calls *back* into your java code for callbacks (for example for logging). |
||||
|
||||
It features a very low overhead, practically non-existant safety checking and allows for pretty performant Java-native interop. In fact, switching from the Java context to a native function context only takes [a few nanoseconds](http://stackoverflow.com/a/13977914/2443595). After that you get the raw performance of native code and all the beautiful header bindings that come with that. |
||||
|
||||
Unfortunately setting this up isn't trivial and outdated documentation/ tutorials, that ignore modern build systems, don't make it easier. And while I will get into the nitty-gritty of what you should do/ avoid in the next article (or two), first I want to outline how java-native interop generally works. |
||||
|
||||
|
||||
### Writing a "native" class |
||||
|
||||
Everything in Java is a class and this is no exception. Thus we first need to provide one that has a few `native` functions that we can then later implement. Other than native functions this class can contain normal java code although I would recommend you don't do that for the sake of readability. |
||||
|
||||
```java |
||||
|
||||
class JniSomething { |
||||
|
||||
static { |
||||
// TODO: Load library |
||||
} |
||||
|
||||
private long something; |
||||
|
||||
public native void initSomething(String name, int count); |
||||
|
||||
public native void doSomething(); |
||||
|
||||
public static native void status(int mode); |
||||
} |
||||
|
||||
``` |
||||
|
||||
Two things that you should notice |
||||
|
||||
1. We have a static initialiser block which we will later use to load a native library |
||||
2. We have a `long` type field in our class which will become important later. For now it shows that "native" classes can have fields just like any other class. |
||||
|
||||
You can also see that we can have static and non-static native functions. Parameters can be primitives and classes although I only demonstrated `String` as a class here (which is a bit of a special case in JNI land) |
||||
|
||||
### Native code generation |
||||
|
||||
We are about a third of the way there. Unfortunately this is where it becomes a little less clean and easy to manage. From our class definition that includes native functions we will now generate a header that we can build native function implementations for. |
||||
|
||||
The tool we will use is called `javah` so make sure it's included in what your operating system packages as java. On most Linux distributions it should come installed with the JDK, on Windows and Mac...I have no idea. Figure it out and e-mail me so I can add it to the article. |
||||
|
||||
Assuming that your "project" structure looks something like this and your current directory is `src` |
||||
|
||||
``` |
||||
🚀 (normandy) ~> tree project/ |
||||
project/ |
||||
└── src |
||||
└── de |
||||
└── spacekookie |
||||
└── JniSomething.java |
||||
``` |
||||
|
||||
then you can generate the jni header file in the project root directory as follows |
||||
|
||||
```console |
||||
javah -verbose -jni -o ../JniSomething.h de.spacekookie.JniSomething |
||||
``` |
||||
|
||||
So far so good. You can look at the file now if you want. It's actually pretty ugly code. The function names are designed to *never* clash with anything else because of how Java and C++ have *very* different approaches when it comes to scoping things. The last part of this intro will be writing code that uses these headers to do something in native code. |
||||
|
||||
|
||||
### Writing & linking native functions |
||||
|
||||
Aaaaand this is where things get really complicated real fast. There are a few things we need to do at this point and none of them are trivial |
||||
|
||||
1. We need to setup our build environment so that we can find the `<jni.h>` utility header |
||||
- Platform specific problems I might add |
||||
- Also very dependant on the build system we might be using later |
||||
2. Write and compile the native code that does *something* and make a library (for us that is an `.so` because Linux) |
||||
3. Load the library correctly as to not crash the JVM |
||||
|
||||
Easy as pie :) |
||||
|
||||
First things first, we need to find the Java home directory. Again, I can only speak from a Linux perspective. But the `$JAVA_HOME` environment variable was depreciated years ago by most distributions. This might not be the best way to find out but it works. |
||||
|
||||
```bash |
||||
# Bash |
||||
echo $(dirname $(readlink -f $(which javac))) |
||||
``` |
||||
```fish |
||||
# fish |
||||
echo (dirname (dirname (readlink -f (which javac)))) |
||||
``` |
||||
|
||||
From the directory we get with these queuries we need to go up one directory and then into a other directory. In the end the path we want to build should look something like this. The second one will obviously be different depending on your platform. |
||||
|
||||
``` |
||||
/usr/lib/jvm/java-8-openjdk-amd64/bin/../include |
||||
/usr/lib/jvm/java-8-openjdk-amd64/bin/../include/linux |
||||
``` |
||||
|
@ -1,366 +0,0 @@ |
||||
Title: 02. Encryption 101: PGP on Mac |
||||
Date: 2013-10-16 16:26 |
||||
Category: Data Privacy |
||||
Tags: Guides |
||||
Slug: 02-encryption-101-pgp-on-mac |
||||
Status: published |
||||
|
||||
Hello Internet, |
||||
|
||||
I started this series about encryption a few weeks ago but then kinda |
||||
ran out of time to actually do something with it so now I want to |
||||
continue it. Essentially this is about PGP and email encryption. This |
||||
tutorial is being inspired by my brothers (much shorter) article about |
||||
the whole thing: |
||||
<http://www.leandersabel.de/itsecurity/e-mail-encryption/>) |
||||
|
||||
What is PGP, you might ask? Well, it's a good question. PGP stands for |
||||
Pretty Good Privacy and uses an asymmetrical encryption concept that you |
||||
should have learned about in the [last blog |
||||
post](http://www.spacekookie.de/01-encryption-101-basics/ "01. Encryption 101: Basics")in |
||||
this series. If you haven't...shame on you! |
||||
|
||||
I want to focus on installing this email encryption on Mac Computers |
||||
first. This is compatible for several versions back. |
||||
|
||||
The asymetric email encryption is based on a zero knowlege principle: |
||||
you send data through the web and except for the recipient of that data |
||||
NOBODY will be able to know what it is. Due to that the encryption needs |
||||
to happen on your device (in this case a Mac computer) and be decrypted |
||||
on an end device again (for example a Windows computer). |
||||
|
||||
It doesn't really matter what e-mail provider you use as you will be |
||||
downloading the mails anyways. The easiest way to do that on a Mac is |
||||
with the pre-installed *Mail* program. If you haven't already get your |
||||
Mail to download mail from your account. If you've done this already you |
||||
can skip ahead to **[Encrypting your Mail](#encryption).** |
||||
|
||||
**Setting up Mail with your account** {style="text-align: justify;"} |
||||
------------------------------------- |
||||
|
||||
Open Mail and click on Mail --\> Preferences |
||||
|
||||
[![mailpgp1](http://www.spacekookie.de/wp-content/uploads/2013/10/mailpgp1.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/mailpgp1.png) |
||||
|
||||
In the upcoming window click on Accounts and then select the "+" sign on |
||||
the bottom |
||||
[![mailpgp2\_1](http://www.spacekookie.de/wp-content/uploads/2013/10/mailpgp2_1.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/mailpgp2_1.png)Another |
||||
window will pop up where you need to enter the apropriate information. |
||||
For large e-mail providers like gmail, yahoo, hotmail, etc. this is |
||||
quite trivial. If you are using a different webhoster you might have to |
||||
**check their FAQs for server information!** |
||||
|
||||
**[![mailpgp3](http://www.spacekookie.de/wp-content/uploads/2013/10/mailpgp3.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/mailpgp3.png)**After |
||||
this is done you should be able to download the e-mails from your |
||||
account to your computer. You may start a celebratory dance now! |
||||
|
||||
Next up: |
||||
|
||||
<a name="encryption"></a> |
||||
|
||||
**Encrypting your Mail |
||||
** {style="text-align: justify;"} |
||||
----------------------- |
||||
|
||||
Now that your emails are being downloaded to your computer we can set |
||||
you up with the encryption software. The one that is the easiest to use |
||||
is called GPG, standing for *GNU Privacy Guard* (with GNU being a linux |
||||
distribution). The software comes in an easy to install package that can |
||||
be found at: [https://gpgtools.org](https://gpgtools.org/) Just scroll |
||||
down to the download button and download the suite to your computer. |
||||
Double click the .dmg file you downloaded and wait for the following |
||||
window to pop up: |
||||
|
||||
[![gpginstall1](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall1.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall1.png) |
||||
|
||||
This should be trivial but double click install :) Another window will |
||||
come up. Be sure to select the right harddrive. It should be installed |
||||
on the harddrive that also contains your operating system. In my case |
||||
the Harddrive is called *TARDIS* (It's bigger on the inside). |
||||
|
||||
[![gpginstall2](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall2.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall2.png) |
||||
|
||||
When the installation is complete eject the installation drive by |
||||
dragging it onto the trash. It's not needed anymore. Open GPG (by either |
||||
searching for it in your Applications folder or using spotlight in the |
||||
top right corner) |
||||
|
||||
[![gpginstall3](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall3.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall3.png) |
||||
|
||||
When you open GPG for the first time it will look somewhat like this for |
||||
you: (Except you won't have any keys in it). |
||||
|
||||
[![gpfinstall4](http://www.spacekookie.de/wp-content/uploads/2013/10/gpfinstall4.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/gpfinstall4.png) |
||||
|
||||
Enter your name, your email adress and check the "Upload public key |
||||
after generation" to make it easier for people to be able to find your |
||||
key. This means that they will be able to send you e-mails encrypted. If |
||||
you don't want that, don't tick it. I recomend it for regular users |
||||
because it makes exchanging keys easier. Press **Generate key** |
||||
to…generate the key (duh). During the generation process move your mouse |
||||
as much as possible and even type random letters on your keyboard. |
||||
|
||||
[![gpginstall5.png](http://www.spacekookie.de/wp-content/uploads/2013/10/Screen-Shot-2013-10-16-at-17.40.57.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/Screen-Shot-2013-10-16-at-17.40.57.png)When |
||||
a window comes up and promts you to enter a password do that so. |
||||
**Choose a strong password as it will be the foundation of your e-mail |
||||
encryption**. The longer and more complicated, the better. The |
||||
application will then take your passphrase and the random input from |
||||
mouse and keyboard to generate a pair of keys: one private, one public. |
||||
|
||||
If you checked it accordingly the public key will be uploaded to the MGU |
||||
servers for other people to find. The public key is used to encrypt |
||||
emails. Other people that have your public key can thus send you a |
||||
message that is encrypted. To decrypt the messages you need your private |
||||
key **that should under no circumstances be sent via the internet or any |
||||
network!** |
||||
|
||||
If you need to move your private key to a second computer for use do so |
||||
on a USB drive or local, external harddrive. **DO NOT STORE YOUR PRIVATE |
||||
KEY IN A CLOUD SERVICE** |
||||
|
||||
[![gpginstall6](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall6.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/gpginstall6.png) |
||||
|
||||
The "sec" indicates that there is a secure (private) key. The "pub" |
||||
stands for a public key. The two combined make a key pair which you |
||||
should only have one of. So far you should be set to communicate so |
||||
let's move onto the next topic: |
||||
|
||||
{style="text-align: justify;"} |
||||
|
||||
**Sending encrypted Mail** {style="text-align: justify;"} |
||||
-------------------------- |
||||
|
||||
Now that this is all set up, what can you do with this? First of all, |
||||
this encryption only works if the other person you're communicating with |
||||
is also using a PGP system (no matter what implimentation or operating |
||||
system). |
||||
|
||||
First of all be sure to restart your Mail application after you |
||||
installed GPG. Otherwise the plugin won't start. Go ahead and compose a |
||||
new email. This is what it should look like now: |
||||
|
||||
[![mailtest1](http://www.spacekookie.de/wp-content/uploads/2013/10/mailtest1.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/mailtest1.png) |
||||
|
||||
The green space on the top right indicates that OpenPGP is active with |
||||
the selected email adress. The little tick in the middle confirmes that |
||||
a signature will be attached to the mail. You can disable to sign your |
||||
emails by clicking on the tick. |
||||
|
||||
If you then enter an email adress of somebody that you own the public |
||||
key from (in my case let me write a mail to my brother Leander) things |
||||
will change a bit: |
||||
|
||||
[![mailtest2](http://www.spacekookie.de/wp-content/uploads/2013/10/mailtest2.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/mailtest2.png) |
||||
|
||||
The lock icon will become active and you will be able to lock it. The |
||||
lock indicates that the email will be encrypted. As the picture also |
||||
indicates the subject of a message is not being encrypted, **only the |
||||
content (including attachments).** Oh and please note that sending |
||||
attachments of multiple gigabytes might take a while to encrypt :) |
||||
|
||||
If I send this message now, let's see what it looks like for the NSA: |
||||
|
||||
``` {.lang:default .decode:true} |
||||
-----BEGIN PGP MESSAGE----- |
||||
Comment: GPGTools - http://gpgtools.org |
||||
|
||||
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 |
||||
qJNMy4CS6/L22o/yiUw= |
||||
=hOZe |
||||
-----END PGP MESSAGE----- |
||||
``` |
||||
|
||||
Yea...not very much :) The longer your key (and message obviously) the |
||||
more and longer jibberish the message will be. And the best thing? It is |
||||
mathematically impossible to reverse engineer the message by generating |
||||
random keys. Because for each encrypted message there is a key that can |
||||
make the original message into any other message. Literally the message |
||||
I just sent my brother could be translated into Shakespeares Hamlet with |
||||
the right key. |
||||
|
||||
A little note: What you see above is what you will see in your mail |
||||
inbox if you access your mails without the Mail program or GPG |
||||
installed. So be sure to follow the tutorial again for any computer you |
||||
might use this on. In my opinion this is the best part, as copanies like |
||||
Google or Microsoft that store your emails will have no idea what you're |
||||
sending things about. |
||||
|
||||
{style="text-align: justify;"} |
||||
|
||||
<a name="exporting"></a> |
||||
|
||||
{style="text-align: justify;"} |
||||
|
||||
**A few last pointers** {style="text-align: justify;"} |
||||
----------------------- |
||||
|
||||
Right clicking onto your key pair in GPG will get you a context menu |
||||
with which you will be able to do a variety of things. Now an |
||||
explanation for the most important ones: |
||||
|
||||
1. Export: saves the key as a text file to a location on your computer. |
||||
You can either export your public key or the pair (public and |
||||
private). Use this to move your private key to a new computer. |
||||
2. Send public key to key server: If you have made changes to your key |
||||
or haven't checked the option before you can upload your key to a |
||||
public server for people to find you. |
||||
3. Update a public key from the keyserver in case you accidentally |
||||
deleted it. You can't update your private key. **You loose it, it's |
||||
gone!** |
||||
4. Show info: displays all kinds of information about the key. You will |
||||
be able to add a new e-mail adress to the key, in case you want to |
||||
send encrypted emails from multiple adresses or change the |
||||
expiration date of the key, etc. |
||||
|
||||
One last important thing: What if you want to import a key to your GPG |
||||
keychain? Take my public key for example: |
||||
|
||||
``` {.lang:default .decode:true} |
||||
-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
Version: GnuPG/MacGPG2 v2.0.19 (Darwin) |
||||
|
||||
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 |
||||
kM773MWawwH7Z3VRQLYT/oweYc6Pd1A= |
||||
=NBpw |
||||
-----END PGP PUBLIC KEY BLOCK----- |
||||
``` |
||||
|
||||
(Downloadable here: |
||||
<http://spacekookie.de/pgp/katharina-sabel-public.asc>) |
||||
|
||||
First open a text editor of your choosing. In my case I will use the |
||||
standard Mac **Text Edit.** Now you need to copy and paste the key |
||||
(From --BEGIN-- to --END--) and paste it into your text |
||||
editor.[![importkeymac1](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac1.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac1.png) |
||||
|
||||
Looks kinda scary, I know. Hang in there. Now save the file with CMD + |
||||
S. In the following popup you need to select to save the file locally |
||||
and not on Apples iCloud servers. They can be great but not for this! |
||||
|
||||
[![importkeymac2](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac2.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac2.png) |
||||
|
||||
Give the file a random name (it's not really important) and save it. |
||||
Next up close the Text Editor and navigate to your saved file. Right |
||||
click on it to bring up the context menu and choose **Get Info** |
||||
|
||||
[![importkeymac3](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac3.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac3.png)In |
||||
the upcoming window then search for the name field and change the |
||||
extention from .rtf to **.asc** |
||||
|
||||
.RTF is a file format for text files and great for stuff. But we want |
||||
the GPG application to recognize all the jibberish as a key and for that |
||||
we need to change the extention to .asc |
||||
|
||||
When your computer prompts you if you're sure you agree and change the |
||||
extention to **.ASC** |
||||
|
||||
[![importkeymac4](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac4.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac4.png) |
||||
|
||||
Now we're almost done. Go to your GPG application, click the IMPORT |
||||
button in the top left and navigate to your key.asc file on your |
||||
computer you just created. Press open and see the magic happen as the |
||||
key is being added to your keychain. |
||||
|
||||
[![importkeymac5](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac5.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/importkeymac5.png) |
||||
|
||||
Now...there is a much easier way to import new keys and that's why I |
||||
kind of insisted on your uploading your public key to a keyserver. If |
||||
you go to your GPG application, select **Key** (in the menu bar on top |
||||
of the screen) and then **Search for key** you will be promted with a |
||||
little window: |
||||
|
||||
[![importkrymac6](http://www.spacekookie.de/wp-content/uploads/2013/10/importkrymac6.png)](http://www.spacekookie.de/wp-content/uploads/2013/10/importkrymac6.png)In |
||||
that window you can search for an email adress or parts of it to find a |
||||
key. To find my public key simply search for katharina.sabel. |
||||
|
||||
I kinda fucked up my keys a few months ago so I have two keys on the |
||||
server. Select the one that was created last (\~August 2013) to add the |
||||
key to your keychain. You won't have to deal with any of the hassle |
||||
including file formats, copy pasting, etc. It's all done. |
||||
|
||||
Feel free to hit me up with a random message to |
||||
sabel.katharina@gmail.com. Be sure to encrypt it, just to test things |
||||
out. And I hope that this tutorial will encourage you to encourage more |
||||
of your family and friends to use encryption. If not for transmitting |
||||
sensible documents like contracts, bills or whatever just to piss off |
||||
the NSA :) |
||||
|
||||
Have a lovely day, |
||||
|
||||
\~Kate |
File diff suppressed because one or more lines are too long
@ -1,216 +0,0 @@ |
||||
Title: 04. (LibGDX) Game of Codes: Textures and asset management |
||||
Date: 2013-12-19 19:18 |
||||
Category: Game of Codes |
||||
Tags: Guides |
||||
Slug: 04-libgdx-game-of-codes-textures-and-asset-management |
||||
Status: published |
||||
|
||||
Welcome back to the Game of Codes, an introduction series to the LibGDX |
||||
framework. In the last edition we learned how to use InputAdapters and |
||||
used the keyboard and mouse to order our little space-ship around on the |
||||
screen. In this issue I want to take a step back from our actual game |
||||
and have a look at our surroundings. Data files, assets and resource |
||||
imports. Where to put them to make the game code as practical and |
||||
readable as possible. LibGDX has a few quirks and also tools to make |
||||
asset management as easy as it can be. So without further ado, let's |
||||
begin. |
||||
|
||||
Collecting all Asset imports in one class {style="text-align: justify;"} |
||||
----------------------------------------- |
||||
|
||||
The first thing we'll want to do is create a new package in our project. |
||||
I named mine "Util" to symbolize that everything in there has to do with |
||||
utility classes and tools that don't have a direct effect on gameplay |
||||
but are substantially important in using the game. If you're not |
||||
familiar with naming conventions for packages here is the rundown. |
||||
|
||||
``` {.lang:java .decode:true} |
||||
SUFFIX.DOMAIN.PROJECT.PACKAGE.SUB-PACKAGE |
||||
|
||||
In my case: |
||||
|
||||
de.spacekookie.libgdxtutorial.util |
||||
``` |
||||
|
||||
If you don't have website where you would publish your code just use |
||||
your name with a com or de or whatever your language code is (or you |
||||
want it to be to confuse people). |
||||
|
||||
![Screen Shot 2013-12-19 at |
||||
15.18.59](http://www.spacekookie.de/wp-content/uploads/2013/12/Screen-Shot-2013-12-19-at-15.18.59.png) |
||||
|
||||
Be sure to select your source folder to create the package. And then in |
||||
the next dialogue type the entire package tree, not just the name of the |
||||
end package you want to name. Because Eclipse will create folders from |
||||
these sub-trees. |
||||
|
||||
[![Screen Shot 2013-12-19 at |
||||
15.19.16](http://www.spacekookie.de/wp-content/uploads/2013/12/Screen-Shot-2013-12-19-at-15.19.16.png)](http://www.spacekookie.de/wp-content/uploads/2013/12/Screen-Shot-2013-12-19-at-15.19.16.png)And |
||||
to not confuse us in the long run r-click on that other package that so |
||||
far holds all other classes and add the subpackage-name "core" to it. It |
||||
will symbolize that it holds our darkest secrets (aka the most important |
||||
classes in our game, like the main game class or the InputAdapter). |
||||
|
||||
When you're done with that go ahead and create a new class in the "util" |
||||
package named along the lines of "ResourceLoader" or "TextureLoader" or |
||||
whatever takes your fancy. I'm gonna call it "**ResPack**" short for |
||||
"ResourcePacker", because it will be filled with static variables that |
||||
we will have to call from all over the game...LOTS of times. So I want |
||||
it to be as short as it can be. Once created go and fill it up with our |
||||
texture loading and save them into "public static final" variables. We |
||||
wan them to be accessible from anywhere in the game "public static" but |
||||
we also want them to be fixed and never changed again (by accident or by |
||||
an evil hacker that uses texture hacks) aka "final". |
||||
|
||||
``` {.lang:java .decode:true} |
||||
package de.spacekookie.libgdxtutorial.util; |
||||
|
||||
import com.badlogic.gdx.Gdx; |
||||
import com.badlogic.gdx.graphics.Texture; |
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion; |
||||
|
||||
/** Class to load all assets for the game */ |
||||
public class ResPack { |
||||
|
||||
public static final TextureRegion shipIdle = new TextureRegion(new Texture( |
||||
Gdx.files.internal("USS_Pixel/ship_idle.png")), 0, 0, 64, 64); |
||||
|
||||
public static final TextureRegion shipFly1 = new TextureRegion(new Texture( |
||||
Gdx.files.internal("USS_Pixel/ship_fly1.png")), 0, 0, 64, 64); |
||||
|
||||
public static final TextureRegion shipFly2 = new TextureRegion(new Texture( |
||||
Gdx.files.internal("USS_Pixel/ship_fly2.png")), 0, 0, 64, 64); |
||||
} |
||||
``` |
||||
|
||||
Alright, that's that so far. In addition to that I've made a little |
||||
texture called "blast\_small" that we will be adding later, which is |
||||
essentially just a little laser shot. You can download it |
||||
[here](http://www.spacekookie.de/downloads/Tutorials/LibGDX1/blast_small.png) |
||||
and add it to your USS\_Pixel folder in the assets directory. |
||||
|
||||
But you can already see a problem, the texture isn't in power of two |
||||
measurements and we would have to manually cut out the blast from a, say |
||||
32x32 texture. But we don't really want that, do we? We're gonna have a |
||||
lot more textures later on and if we have to cut out each one |
||||
individually we're gonna have a hard time. And that's why we have such a |
||||
handy thing called the |
||||
[TextureAtlas](http://en.wikipedia.org/wiki/Texture_atlas). |
||||
|
||||
Working with TextureAtli {style="text-align: justify;"} |
||||
------------------------ |
||||
|
||||
A TextureAtlas is essentially a file that contains all resources in it |
||||
packed into one image and an .atlas file that keeps tabs on what |
||||
resource can be found at what exact pixel location (and it's size, etc). |
||||
To create a TextureAtlas we need a TexturePacker which isn't included in |
||||
its GUI form in the LibGDX distribution (we can learn how to use the |
||||
command-line class from within our game later). Go |
||||
[here](https://code.google.com/p/libgdx-texturepacker-gui/downloads/list) |
||||
to download the GUI and add it somewhere in your project folder, why not |
||||
inside the asset folder. |
||||
|
||||
The texturepacker is a .jar that can be run as a UI application that |
||||
needs no setting up. Run it and get a look around. First we want to |
||||
create a new pack, so do that on the top-left corner of the window. |
||||
Select a project name, an input, an output directory and of course a |
||||
file-name pattern. Input is the USS\_Pixel folder in our assets folder, |
||||
the output is the assets folder itself (in my case). Leave all the other |
||||
settings as they are. Then select "Pack 'em all" and wait for a |
||||
confirmation to pop up. |
||||
|
||||
![TexturePackerDemo1](http://www.spacekookie.de/wp-content/uploads/2013/12/Screen-Shot-2013-12-19-at-16.39.00.png) |
||||
|
||||
Go and check the output folder where you should see a .png and an .atlas |
||||
file. If you have a look at the .png you will see that all the single |
||||
textures we had in our USS\_Pixel directory have been molded into a |
||||
single file that has the power-of-two resolution so that our game won't |
||||
die on us. The .atlas file should just be a bunch of numbers and words |
||||
with parameters. We'll have a look at those in a minute. First I want |
||||
you to update Eclipse so that it sees the new files in our asset folder |
||||
and direct your attention back at the ResPack class that we started in |
||||
the beginning of this tutorial. |
||||
|
||||
We'll want to create a TextureAtlas object, private, static and final |
||||
and give it the .atlas file to read. I will call mine "\_PIXEL" |
||||
|
||||
*(It's my naming convention to give variables that are only used in very |
||||
very (VERY) specific context an underscore as a beginning so that they |
||||
show up first on the list of recommended variables but also to filter |
||||
them out quickly and give them somewhat of a specific look. You don't |
||||
have to do it this way, I think it's even considered bad practise. But |
||||
it's something that I like to do).* |
||||
|
||||
After we have our private static and final TextureAtlas we can find |
||||
textures in it by calling "findRegion(. . .)" on it. Here is some code |
||||
as reference. |
||||
|
||||
``` {.lang:java .decode:true} |
||||
private static final TextureAtlas _PIXEL = new TextureAtlas( |
||||
Gdx.files.internal("USS_Pixel.atlas")); |
||||
|
||||
public static final TextureRegion shipIdle = _PIXEL |
||||
.findRegion("ship_idle"); |
||||
``` |
||||
|
||||
As you can see we no longer have to bother around with pixel coordinates |
||||
in our textures because they've all be stored in the .atlas file by the |
||||
TexturePacker. All there is left to do now is remove the TextureRegion |
||||
calls from our Entity class and change it the fly() and idle() methods |
||||
as well. |
||||
|
||||
``` {.lang:java .decode:true} |
||||
public void loadResources() { |
||||
if (type.equals(EntityType.PLAYER)) { |
||||
|
||||
self = new Sprite(ResPack.SHIP_IDLE); |
||||
} |
||||
} |
||||
|
||||
public void idle() { |
||||
self.setRegion(ResPack.SHIP_IDLE); |
||||
} |
||||
|
||||
public void fly() { |
||||
int i = new Random().nextInt(2); |
||||
if (i == 0) |
||||
self.setRegion(ResPack.SHIP_IDLE); |
||||
else if (i == 1) |
||||
self.setRegion(ResPack.SHIP_FLY1); |
||||
else if (i == 2) |
||||
self.setRegion(ResPack.SHIP_FLY2); |
||||
} |
||||
``` |
||||
|
||||
And that's that. Isn't it neat? We cleaned up our code and even made the |
||||
loading of textures easier and more efficient as well. The only downside |
||||
is that you will have to re-pack your TextureAtlas for every texture |
||||
that you add. In practise I usually have 2-3 atli that hold different |
||||
kinds of textures together to keep some orientation over the textures |
||||
involved. |
||||
|
||||
For that purpose I created three more textures, the Deep\_Pixel\_Nine, |
||||
the Pixel\_Sun and Pixel\_Earth to be part of an "environment" atlas (as |
||||
well as a more low-profile space picture :p ). You can download the |
||||
whole .zip file with everything inside |
||||
[here](http://www.spacekookie.de/downloads/Tutorials/LibGDX1/world.zip)! |
||||
Go and add that to the ResPacker. You should know how ;) |
||||
|
||||
After setting up your ResPacker to properly import all the |
||||
TextureRegions you should go and change the image that we're currently |
||||
using and clean up your code in a way that it won't crash and doesn't |
||||
contain calls that aren't being used anymore. With the ResPacker you |
||||
have all your resources in one place and collected in two Texture Atli. |
||||
|
||||
Lastly, I went ahead and refactored the assets folder a little to be |
||||
less clustered and deleted some of the files that we didn't need any |
||||
more. Also note that I'm always keeping the raw files in case I need to |
||||
update something. |
||||
|
||||
![refactored\_assets\_1](http://www.spacekookie.de/wp-content/uploads/2013/12/refactored_assets_1.png) |
||||
|
||||
And that's that for our rather short issue this time (I would want to |
||||
say this week but I'm writing these so quickly right now). Next time I |
||||
want to have a look at...I'm not sure yet. Probably cameras, controlling |
||||
cameras, moving cameras around, etc. And of course some lovely camera |
||||
theory. But until then have a lovely day and keep coding! |
@ -1,28 +0,0 @@ |
||||
Title: 09.5 (LibGDX) Game of Codes: Continuation - Post ideas |
||||
Date: 2015-05-02 12:12 |
||||
Category: Game of Codes |
||||
Tags: Guides |
||||
Slug: continue-libgdx-game-of-codes |
||||
Status: published |
||||
|
||||
Welcome back to the Game of Codes, an introduction series to the LibGDX |
||||
framework. In the last post we started messing around with the inner |
||||
workings of LibGDX a bit more... |
||||
|
||||
\*static\* \*voice cracking\* \*buzzing from speakers\* |
||||
|
||||
"Hello? Can anyone hear me? I think I'm in space...Hello?" |
||||
|
||||
Right...it's been about a year since I wrote on this series, a lot of |
||||
things have changed and I don't nearly have as much time anymore. BUT |
||||
people keep messaging me all over the place that they liked my LibGDX |
||||
series. SO...if you really do, I will continue it. Though, I'm slightly |
||||
out of ideas what to do. And I really have lost interest in continuing |
||||
that Star Chaser thing I started. |
||||
|
||||
So....if you want to see something from me, post it on the comments |
||||
below, twitter at me, send me emails or smoke signals. If I get enough |
||||
feedback about this I will do it. If not, I'm sure there are better and |
||||
more up to date resources out there :) |
||||
|
||||
So yea, would be cool to hear from you guys \<3 |
@ -1,7 +0,0 @@ |
||||
Title: 10 (LibGDX) Game of Codes: 50 Shades of Code |
||||
Category: Game of Codes |
||||
Tags: Guides |
||||
Slug: libgdx-game-of-codes-50-shades-of-code |
||||
Status: draft |
||||
|
||||
SHADERS! |
Binary file not shown.
Loading…
Reference in new issue