AS3 Creating a drawing app using bitmapdata - actionscript-3

I'm trying to create a drawing application that renders vector lines into bitmap. I've read the documentation about bitmapdata and I have a basic understanding of how it should work. But I'm having some trouble. As of right now my goal is simple, allow the user to draw lines with their mouse, that's all I want. The problem is somewhere with the matrix i'm using, could someone help me out?
import flash.display.Sprite; //imports needed
import flash.events.Event;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.events.MouseEvent;
import flash.geom.Matrix;
var draw:Boolean = false; //Boolean to determine when the mouse is down since bitmapdata doesnt receieve mouse events.
var brush:Sprite =new Sprite(); // Creating the "brush", determining the stroke it will make.
brush.graphics.lineStyle(0x000000);
brush.graphics.lineTo(mouseX,mouseY);
var data:BitmapData = new BitmapData(600,400, false); // Creating bitmapdata to allow the work with pixels.
var canvas:Bitmap = new Bitmap(data);
addChild(canvas);
stage.addEventListener(MouseEvent.MOUSE_DOWN, drawStart); // Event listeners to determine when the mouse is up or down.
stage.addEventListener(MouseEvent.MOUSE_UP, drawStop);
stage.addEventListener(Event.ENTER_FRAME, render);
function drawStart(e:MouseEvent):void // When the mouse is down we are drawing
{
draw= true;
}
function drawStop(e:MouseEvent):void // When the mouse is up we are not drawing
{
draw= false;
}
function render(e:Event):void //Rendering the vector into bitmap
{
if(!draw) return;
var mat:Matrix=new Matrix(); // We need a matrix to get the correct mouse coordinates
mat.translate(mouseX,mouseY)
data.draw(brush,mat); // Then we draw the bitmap into vector.
}
I have listed comments to show what I understand is happening. If I have gotten something wrong i'd like it if someone could explain it better to me.
When tested, the program draws lines, but all it does is draw a line to the mouse position from some other seemingly random position. So I figure the problem has something to do with the matrix.
I appreciate any help I can get, i've been looking at this for a while and it's just not clicking. Thanks.

The main problem with your code is that you draw the line into your bush.graphics only once (when your app starts), before any user input and then draw that same line into your bitmap data every frame as long as the mouse is down.
One way to do things correctly would be to redraw that line every frame while the user keeps the mouse key down. The drawing should happen in your brush.graphics (which now serves more like a canvas) and finally, once the user releases the mouse the line he has drawn should be rendered into the bitmap data so you can reuse your brush.graphics for drawing new lines.
var draw:Boolean = false; //Boolean to determine when the mouse is down since bitmapdata doesnt receieve mouse events.
var brush:Sprite;
var canvas:Bitmap;
var data:BitmapData;
var start:Point = new Point();
brush = new Sprite(); // This will serve as a canvas
data = new BitmapData(600,400, false); // Creating bitmapdata to allow the work with pixels.
canvas = new Bitmap(data);
addChild(canvas);
addChild(brush); // Add to display list so we can see what we are drawing visually
stage.addEventListener(MouseEvent.MOUSE_DOWN, drawStart); // Event listeners to determine when the mouse is up or down.
stage.addEventListener(MouseEvent.MOUSE_UP, drawStop);
stage.addEventListener(Event.ENTER_FRAME, render);
private function drawStart(e:MouseEvent):void // When the mouse is down we are drawing
{
draw = true;
start.setTo(e.localX, e.localY); // Save mouse position at interaction start
}
private function drawStop(e:MouseEvent):void // When the mouse is up we are not drawing
{
draw = false;
data.draw(brush, null); // User released the mouse and we can draw the result into bitmap
}
private function render(e:Event):void //Rendering the vector into bitmap
{
if(!draw) return;
// Redraw the line each frame as long as the mouse is down
brush.graphics.clear();
brush.graphics.lineStyle(0x000000);
brush.graphics.moveTo(start.x, start.y);
brush.graphics.lineTo(mouseX, mouseY);
}

Related

Stretch and rotate a Movieclip without distortion

i'm building a flash desktop app, where the user needs to link two Movieclips on stage (a computer and a router) using a line (or whatever can do the job), i want to achieve this same exact effect: image1. I searched and found this solution, i tried the code and did some modifications:
link.addEventListener(MouseEvent.CLICK, linkOnClick);
function linkOnClick(e:MouseEvent){
this.addEventListener(Event.ENTER_FRAME, enterFrame);
var linkPoint:Point = new Point(link.x, link.y);
var mousePoint:Point = new Point();
var distance:Number;
var radians:Number;
function enterFrame(e:Event):void {
//Distance
mousePoint.x = stage.mouseX;
mousePoint.y = stage.mouseY;
distance = Point.distance(linkPoint, mousePoint);
link.width = distance;
//Rotation
radians = Math.atan2(stage.mouseY - link.y, stage.mouseX - link.x);
link.rotation = radians * (180/ Math.PI);
if(link.hitTestObject(router)){trace("Success");}
}
When i compiled the code i got this: image2, so as you may remark, the problems i found are:
1-the edge of the line follows the direction of the mouse, but sometimes it goes beyond the cursor, i want the cursor to drag the edge of the line.
2-the line changes it's width, if it's 90° degrees the line width is so remarkable, i want the line to have a constant width.
how can i acheive the same exact effect shown in image1 ?
// First, lets create mouse-transparent container for drawing.
var DrawingLayer:Shape = new Shape;
addChild(DrawingLayer);
// Hook the event for starting.
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
// Define a storage for keeping the initial coordinates.
var mouseOrigin:Point = new Point;
function onDown(e:MouseEvent):void
{
// Save the initial coordinates.
mouseOrigin.x = DrawingLayer.mouseX;
mouseOrigin.y = DrawingLayer.mouseY;
// Hook the events for drawing and finishing.
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onDraw);
}
function onDraw(e:MouseEvent):void
{
// Remove the previous line.
DrawingLayer.graphics.clear();
// Draw a new line.
DrawingLayer.graphics.lineStyle(5, 0xFF6600);
DrawingLayer.graphics.moveTo(mouseOrigin.x, mouseOrigin.y);
DrawingLayer.graphics.lineTo(DrawingLayer.mouseX, DrawingLayer.mouseY);
}
function onUp(e:MouseEvent):void
{
// Unhook the events for drawing and finishing.
stage.removeEventListener(MouseEvent.MOUSE_UP, onUp);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDraw);
}
It's because of that the actionscript is trying to stretch the line thickness by changing its container MovieClip's scale. But you can prevent this by setting the line Scale option to None.
To do that, select your line and open the properties menu and then select None from the drop down menu of the Scale option.
But,
I recommend you to draw a line by a code: Draw line from object to Mouse (AS3)
Write below code:
this.graphic.clear ();
this.graphic.lineStyle(0x000000);
this.moveTo(startPoint.x,startPoint.y);
this.lineTo(endpoint.X,endpoint.y);

Collision of drawn line with PNG

I'am working on maze game and don't understand how can I create collision of drawn line with .png.
My maze itself is a .png file where waypaths represented with transparent part. Non-transparent part represents walls of a maze. Player's goal is to draw line walking on transparent part and don't hit walls.
My drawn line also contains BitmapDataStyle. Here's the code:
import flash.events.*;
import flash.display.*;
stage.addEventListener(Event.ENTER_FRAME, f_enterFrameHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, f_mouseUp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, f_mouseDown);
var m = new MovieClip();
var w = stage.addChild(m);
var g = m.graphics;
var misdown = false;
var bitmap:BitmapData = new mark_line;
g.lineStyle(6,0x444444,1);
g.lineBitmapStyle(bitmap);
g.moveTo(0,0);
function f_enterFrameHandler(e:Event)
{
if (misdown)
f_drawLine();
}
function f_mouseUp(e:MouseEvent)
{
misdown = false;
}
function f_mouseDown(e:MouseEvent)
{
misdown = true;
g.moveTo(w.mouseX,w.mouseY);
}
function f_drawLine()
{
g.lineTo(w.mouseX,w.mouseY);
}
You have to detect hit of two shapes, you have to use bitmapData.hitTest(). you can detect collision between any shapes from their bitmapData. to do that, you have to draw both of your shapes on bitmapData like line belo:
var shape1Bitmap:BitmapData = new BitmapData(shape1MC.with,shape1MC.height,true,0x000000);
shape1Bitmap.draw(shape1MC);
var shape2Bitmap:BitmapData = new BitmapData(shape1MC.with,shape1MC.height,true,0x000000);
shape1Bitmap.draw(shape1MC);
shape1Bitmap.hitTest(new Point(),shape2Bitmap):Boolean;
to continue using BitmapData.hitTest(), follow the orders here : https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#hitTest()
http://dougmccune.com/blog/2007/02/03/using-hittestpoint-or-hittest-on-transparent-png-images/
Good luck

Custom Mouse Cursor dropping duplicate symbols after its been removed

first of all, I'm a total noob to as3 and coding in general, I barely operate outside of code snippets.
I'm working on a project, and part of which is a scene where you get a custom mouse cursor upon entering the scene, and when you leave the scene, the custom mouse cursor is removed. The code I'm using to start the custom cursor is:
stage.addChild(crsTemple);
crsTemple.mouseEnabled = false;
crsTemple.addEventListener(Event.ENTER_FRAME, fl_CustomMouseCursor);
function fl_CustomMouseCursor(event:Event)
{
crsTemple.x = stage.mouseX;
crsTemple.y = stage.mouseY;
}
Mouse.hide();
with crsTemple being the instance name for the custom cursor. Then, when a new scene is entered (via rolling over an object), i have the following code in the new scene:
stage.addChild(crsTemple);
crsTemple.mouseEnabled = false;
crsTemple.addEventListener(Event.ENTER_FRAME, fl_CustomMouseCursor_4);
function fl_CustomMouseCursor_4(event:Event)
{
crsTemple.x = stage.mouseX;
crsTemple.y = stage.mouseY;
}
Mouse.hide();
crsTemple.removeEventListener(Event.ENTER_FRAME, fl_CustomMouseCursor_4);
stage.removeChild(crsTemple);
Mouse.show();
Unfortunately, whenever I go into the second scene, I get the regular mouse again, but it drops the crsTemple wherever the mouse was when the scene change happened, and it stays there for the rest of the time the file is running.
Any help is greatly appreciated, much thanks in advance for helping a noob like me!
No need to write the same code in new Scene. You can actually use all declarations form the first Scene. In the following code snippet MOUSE_MOVE handler (fl_CustomMouseCursor) from scene 1 will be called in scene 2 either. Custom cursor will also be accessible by its name crsTemple.
import flash.display.MovieClip;
import flash.events.MouseEvent;
var crsTemple:Sprite = new CrsTemple();
crsTemple.mouseEnabled = false;
addChild(crsTemple);
// for smooth cursor movement MOUSE_MOVE instead of ENTER_FRAME
stage.addEventListener(MouseEvent.MOUSE_MOVE, fl_CustomMouseCursor);
stage.addEventListener(MouseEvent.CLICK, nextStage); // for test purpose, just to switch the stage
function fl_CustomMouseCursor(event:Event):void
{
crsTemple.x = stage.mouseX;
crsTemple.y = stage.mouseY;
trace(crsTemple.x);
}
function nextStage(e:Event):void {
gotoAndStop(1,"Scene 2");
}
Mouse.hide();
stop();
here is a link to fla sample

bend line 90 degrees at a point where another movieclip intersects with it in actionscript 3

Essentially, I'm making a game in Flash CS6 where a light source is projected onto the screen. You get a mirror that you can drag around. When the mirror touches the light, however, it should bounce off it and end up 90 degrees offset after it hit the mirror.
I don't have enough reputation to post pictures, but here's a link to an explanation of the problem: http://raphaelhennessy.com/misc/explanation.png
If you can help me solve this I would be really happy.
Thanks in advance,
-Raph
This is an extremely quick and dirty example to get the ball rolling. There are many issues with this code, so I suggest you use it only as a jumping off point. Details are commented within the code.
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class MirrorTest extends Sprite
{
private var _mirror:Sprite;
private var _line:Sprite;
public function MirrorTest()
{
super();
// create the line clip
_line = addChild(new Sprite()) as Sprite;
_line.graphics.clear();
_line.graphics.lineStyle(3, 0xFF0000);
_line.graphics.moveTo(0, 0);
_line.graphics.lineTo(500, 0); // starting it off at an arbitrary width
_line.y = 100; // positioning line so that we can see when it bends upwards
// create the mirror clip
_mirror = addChild(new Sprite()) as Sprite;
// draw a square and rotate it so we have a diamond shape
_mirror.graphics.beginFill(0);
_mirror.graphics.drawRect(-20, -20, 40, 40);
_mirror.graphics.endFill();
_mirror.rotation = 45;
_mirror.x = _mirror.y = 200; // position the mirror away from the line
// add even listeners to trigger dragging/dropping
_mirror.addEventListener(MouseEvent.MOUSE_DOWN, dragMirror);
_mirror.addEventListener(MouseEvent.MOUSE_UP, dropMirror);
}
private function dragMirror($event:MouseEvent):void
{
// start dragging the mirror.
_mirror.startDrag(true);
// add the ENTER_FRAME listener so that we can check for colision as the mirror is being moved around
addEventListener(Event.ENTER_FRAME, onTick);
}
private function dropMirror($event:MouseEvent):void
{
// stop dragging the mirror
_mirror.stopDrag();
// remove the ENTER_FRAME listener so we don't waste cycles checking for collision when the mirror is not being moved around
removeEventListener(Event.ENTER_FRAME, onTick);
}
private function onTick($event:Event):void
{
// check to see if the mirror has collided with the line
if (_mirror.hitTestObject(_line))
{
// if so, redraw the line as a right-angle, using the mirror's position as the "collision point"
_line.graphics.clear();
_line.graphics.lineStyle(3, 0xFF0000);
_line.graphics.moveTo(0, 0);
_line.graphics.lineTo(_mirror.x - _mirror.width * .5, 0);
_line.graphics.lineTo(_mirror.x - _mirror.width * .5, -100);
}
else
{
// if not, redraw the original line
_line.graphics.clear();
_line.graphics.lineStyle(3, 0xFF0000);
_line.graphics.moveTo(0, 0);
_line.graphics.lineTo(500, 0);
}
}
}
}

Actionscript hitTest drawing

I've gotten actions on a frame, what I'm trying to do is have a hitTest that triggers gotoAndStop(<lose frame>) when the shape I am drawing collides with the touchTest. The only issue I'm having is I cannot get the hitTest to register directly when the line hits it, it only registers after the next click event. The other issue I'm encountering is a hit box on the touchTest is many times larger than the actual image of the symbol.
var myshape:Shape;
myshape = new Shape();
myshape.graphics.lineStyle(5, 0xC807DE);
var alreadyDrawn:Shape;
alreadyDrawn = new Shape();
stage.addEventListener(MouseEvent.MOUSE_DOWN, activateDraw);
function activateDraw(event:MouseEvent):void
{
myshape.graphics.moveTo(mouseX,mouseY);
addChild(myshape);
stage.addEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
stage.addEventListener(MouseEvent.MOUSE_UP, stopDraw);
}
function lineDraw(event:MouseEvent):void
{
myshape.graphics.lineTo(mouseX,mouseY);
checkIt();
}
function stopDraw(event:MouseEvent):void
{
alreadyDrawn.graphics.copyFrom(myshape.graphics);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDraw);
}
function checkIt()
{
if (alreadyDrawn.hitTestObject(touchTest) == true)
{
trace("wall");
myshape.graphics.clear();
myshape.graphics.lineStyle(5, 0xC807DE);
alreadyDrawn.graphics.clear(); // clear this too
stopDraw(null); // stop active draw, if any
}
}
it only registers after the next click event
This is because the object you are testing the collision against alreadyDrawn doesn't have a collision area yet. You create the new shape, add your listeners, and test your collision in your lineDraw() using the method checkIt(), but the shape doesn't have a collision area until your mouse up function stopDraw() where it does alreadyDrawn.graphics.copyFrom(myshape.graphics);
So to fix this you would have to create the graphics object earlier. The change could look something like this (at the top):
var alreadyDrawn:Shape = new Shape();
alreadyDrawn.graphics.copyFrom(myshape.graphics);
That would give a collision area to test against in checkIt()
The other issue I'm encountering is a hit box on the touchTest is many
times larger than the actual image of the symbol.
For this issue, you can access the clip or a symbol inside it and grab its bounds relative to the parent of the alreadyDrawn shape. Then you can use the bounds of both shapes to test for a collision. This will give you a more accurate collision area for testing:
function checkIt()
{
var alreadyDrawnBounds:Rectangle = alreadyDrawn.getBounds( alreadyDrawn.parent );
var testBounds:Rectangle = touchTest.someSymbolName.getBounds( alreadyDrawn.parent );
//could also try this instead:
//var alreadyDrawnBounds:Rectangle = alreadyDrawn.getBounds( touchTest.parent );
//var testBounds:Rectangle = touchTest.getBounds( touchTest );
if ( alreadyDrawnBounds.intersects( testBounds ) ) {
trace("wall");
myshape.graphics.clear();
myshape.graphics.lineStyle(5, 0xC807DE);
alreadyDrawn.graphics.clear(); // clear this too
stopDraw(null); // stop active draw, if any
}
}