DroidScript Images

In this post I will speak about images in DroidScript.
They are the easiest way to display image files and also for drawing basic shapes like lines, rectangles, ellipses and even other images.
The possibilities are endless - you can make image viewers, drawing apps, animations, visualizations, games and even your own controls.

Content

  1. Load image files
  2. Basic Drawing
  3. Touch events
  4. Animating
  5. Image drawing and alias

Load image files

This is the most common usage of images - displaying. You can't just display png or jpg files - also gif's are possible!
//Called when application is started. function OnStart() { //Create a layout with objects vertically centered. var lay = app.CreateLayout( "linear", "VCenter,FillXY" ); //Create png image and add it to layout png = app.CreateImage("/Sys/Img/Hello.png"); lay.AddChild(png); //Create gif image and add it to layout gif = app.CreateImage("/assets/edit/browse/Img/ajax-loader.gif", 0.1); lay.AddChild(gif); //Add layout to app. app.AddLayout( lay ); }

Basic Drawing


This is a very useful feature of the image object which is not available on any other controls. But you should follow some rules for that:

- Creating blank images with null as first parameter requires width and height parameters

- Use as less drawing commands as possible

drawing takes much time - especially on images. So you should prevent switching between colours, drawing modes and line widths very often. It has a great impact on the performance!

- Always use SetAutoUpdate(false); and img.Update();

If you don't you can literally watch every single drawing command. Of course there may be some rare cases where you don't need it - but PLEASE don't!
You shouldn't forget to update the image after you've drawn everything. Otherwise you won't see your great arts ;)

- there's a Clear(); command

especially in game development you'll have to redraw your game very often - Clear() will delete everything for you!


Here's an example of a smiley:
function OnStart() { app.SetOrientation("portrait"); var lay = app.CreateLayout( "linear", "VCenter,FillXY" ); //Create empty image and add it to layout img = app.CreateImage(null, 1, 1); img.SetAutoUpdate(false); lay.AddChild(img); app.AddLayout( lay ); //face img.SetPaintColor("yellow"); img.DrawCircle(.5, .5, .5); //left eye img.SetPaintColor("white"); img.DrawArc(.2, .4, .3, .5, 0, 360); //right eye img.DrawArc(.7, .4, .8, .5, 0, 360); //left iris img.SetPaintStyle("fill"); img.SetPaintColor("grey"); img.DrawCircle(.25, .45, .05); //right iris img.DrawCircle(.75, .45, .05); //left pupil img.SetPaintColor("black"); img.DrawCircle(.25, .45, .025); //right pupil img.DrawCircle(.75, .45, .025); //face border img.SetPaintStyle("line"); img.SetLineWidth(5); img.DrawCircle(.5, .5, .5); //mouth img.DrawArc(.2, .4, .8, .7, 45, 90); //left eye border img.DrawArc(.2, .4, .3, .5, 0, 360); //right eye border img.DrawArc(.7, .4, .8, .5, 0, 360); //update image img.Update(); }

Touch events

In many cases you need touch events to allow user input - in games as well as drawing apps, custom controls or even just for touch listeners.
Your callback method will get an event object as first parameter. It includes following values:

- source: the image object which was touched (also accessable via 'this' or the variable name if available)
- action: can be either "Down", "Move", or "Up" - represents the touch event type
- X : screen relative float of the x-coordinate of the first touch position
- Y : screen relative float of the y-coordinate of the first touch position
- x : array of screen relative floats of the x-coordinates of the first three touch positions
- y : array of screen relative floats of the y-coordinates of the first three touch positions
- count : integer value between (including) 1 and 3 which counts how many touches there are

const lineWidth = 10; var tev, r; function OnStart() { app.SetOrientation("portrait"); var lay = app.CreateLayout( "linear", "VCenter,FillXY" ); img = app.CreateImage(null, 1, 1); img.SetAutoUpdate(false); img.SetLineWidth(lineWidth); img.SetOnTouch(img_OnTouch) lay.AddChild(img); app.AddLayout( lay ); //save width of line as screen relative value (for DrawCircle) r = lineWidth/img.GetAbsWidth(); } function img_OnTouch(ev) { //draw line (after tev saved) if(ev.action != "Down") img.DrawLine(tev.X, tev.Y, ev.X, ev.Y); //round line edges (comment it out to see the difference) img.DrawCircle(ev.X, ev.Y, r); //save event tev = ev; img.Update(); }

Animating

Animations are often used for games. They're nearly indispensable for them. For DroidScript there are two common ways to do that.
You can either use JavaScript's setInterval:
var wh, diff, ttime = 0, ball = new Ball(0.5, 0.5, 0.05, 0.5, 0.7); function OnStart() { app.SetOrientation("portrait"); app.SetScreenMode("Game"); var lay = app.CreateLayout( "linear", "VCenter,FillXY" ); img = app.CreateImage(null, 1, 1); img.SetAutoUpdate(false); img.SetTextSize(10); lay.AddChild(img); app.AddLayout( lay ); //get image aspect wh = img.GetAbsWidth() / img.GetAbsHeight(); //start core loop with 30fps setInterval(loop, Math.floor(1000/30)); //init time difference getTimeDiff(); } //core loop function loop() { moveObjects(); drawObjects(); } function moveObjects() { diff = getTimeDiff(); //check wether ball will collide in next frame var tball = ball.getNextPos(diff); if(tball.x - tball.r < 0 || tball.x + tball.r > 1) ball.vx = -ball.vx + Math.random() * 0.02 - 0.01; if(tball.y - wh * tball.r < 0 || tball.y + wh * tball.r > 1) ball.vy = -ball.vy + Math.random() * 0.02 - 0.01; ball.move(diff); } function drawObjects() { img.Clear(); ball.draw(img); //calculate frames per second img.DrawText(Math.floor(1/diff).toString() + " fps", 0.01, 0.99); img.Update(); } function getTimeDiff(t) { var d = new Date().getTime() / 1000 - ttime; ttime += d; return d; } //ball class function Ball(x, y, r, vx, vy) { this.x = x; this.y = y; this.r = r; this.vx = vx; this.vy = vy; this.move = function(dt) { this.x += this.vx * dt; this.y += this.vy * dt; } //returns a moved copy of the ball this.getNextPos = function(dt) { var b = new Ball(this.x, this.y, this.r, this.vx, this.vy); b.move(dt); return b; } this.draw = function(img) { img.DrawCircle(this.x, this.y, this.r); } }

Or DroidScripts Animate(); function which has the time difference as default parameter.
When using app.Animate you can also use the "NoDom" option which removes most javasctipt builtin objects to increase performancs. That means that you can't use setInterval() any more for instance.
_AddOptions("NoDom"); var wh, diff, ball = new Ball(0.5, 0.5, 0.05, 0.5, 0.7); function OnStart() { app.SetOrientation("portrait"); app.SetScreenMode("Game"); var lay = app.CreateLayout( "linear", "VCenter,FillXY" ); img = app.CreateImage(null, 1, 1); img.SetAutoUpdate(false); img.SetTextSize(10); lay.AddChild(img); app.AddLayout( lay ); //get image aspect wh = img.GetAbsWidth() / img.GetAbsHeight(); //start core loop with 30fps app.Animate(loop, 30); } //core loop function loop(t, d) { diff = d / 1000; moveObjects(); drawObjects(); } function moveObjects() { //check wether ball will collide in next frame var tball = ball.getNextPos(diff); if(tball.x - tball.r < 0 || tball.x + tball.r > 1) ball.vx = -ball.vx + Math.random() * 0.02 - 0.01; if(tball.y - wh * tball.r < 0 || tball.y + wh * tball.r > 1) ball.vy = -ball.vy + Math.random() * 0.02 - 0.01; ball.move(diff); } function drawObjects() { img.Clear(); ball.draw(img); //calculate frames per second img.DrawText(Math.floor(1/diff).toString() + " fps", 0.01, 0.99); img.Update(); } //ball class function Ball(x, y, r, vx, vy) { this.x = x; this.y = y; this.r = r; this.vx = vx; this.vy = vy; this.move = function(dt) { this.x += this.vx * dt; this.y += this.vy * dt; } //returns a moved copy of the ball this.getNextPos = function(dt) { var b = new Ball(this.x, this.y, this.r, this.vx, this.vy); b.move(dt); return b; } this.draw = function(img) { img.DrawCircle(this.x, this.y, this.r); } }

Image drawing and alias

Games often use sprites for their animationns. And in some cases you may want to use pixelated sprites.
Both is possible this way:
function OnStart() { app.SetOrientation("landscape"); var lay = app.CreateLayout( "linear", "VCenter,FillXY" ); img = app.CreateImage(null, 1, 1); lay.AddChild(img); app.AddLayout( lay ); var wh = img.GetAbsWidth() / img.GetAbsHeight(); //create image without adding it to lay sprite = app.CreateImage("/Sys/Img/AScript.png", -1, -1, "alias"); //draw image 45 degrees rotated img.DrawImage(sprite, .2, .2, .6, .6*wh, 45); }

Comments

Popular posts from this blog

Creating DroidScript Plugins on Mobile with AIDE - 2 (DS SDK)

Creating DroidScript Plugins on Mobile with AIDE

Creating DroidScript Plugins on Desktop PC with Android Studio