I've been toying with flash for a bit, I usually solve my problems fairly quick, however I have been stuck on this one for a few days, google hasn't been useful, and I do not have friends that know much about flash.
So, I've been working on making a basic 2D platform. I'm having problems figuring out how to implement the points on the character and bg.hitTestPoint. As it stands, before I worked the points and everything in, it was working fine. I have a trace in, to let me know when it considers it a collision with the object (In this case a wall).
The character's dimensions are:
Top Center: (42,0)
Bot Center: (42,112)
R. Center: (84,56)
L. Center: (0,56)
Total Image: 84 x 112 (px)
The character's location on the screen:
Top Center: (46, 460)
Bot Center: (46, 379)
L. Center: (20, 440)
R. Center: (80,420)
Here are the blocks:
The bump/collision variables:
var leftB:Boolean = false;
var rightB:Boolean = false;
var topB:Boolean = false;
var botB:Boolean = false;
var topBPt:Point = new Point(42, 0);
var botBPt:Point = new Point(42, 112);
var leftBPt:Point = new Point(0, 56);
var rightBPt:Point = new Point(84, 56);
The hitTestPoint block: (To save space, I'll just do the top one.)
if(bg.hitTestPoint(fag.x + topBPt.x, fag.y + topBPt.y, true))
{
trace("Top Bump works.");
topB = true;
}
else
{
topB = false;
}
if(topB)
{
if(ySpeed < 0)
{
ySpeed *= -0.5;
}
}
I tried to use different dimensions, there is a small chance I may just not have understood the full explanation on the site I was reading.
Also, I have a feeling I can work around it by just making it a hitTestObject, and manually making each part of the wall different...But I don't see that as efficient or reasonable. Thanks for any help.
The math for finding out the collision for rectangular areas is generally the same.
First of all after glancing your code, define your screen objects as objects... I mean sublass Sprite class and add properties to it, do not do it via globals...
Like:
public class myScreenObjs extends Sprite {
// Add the tricky cool specialities and properties here
}
Normally a sprite exposes its x and y coordinates. If dynamically created x and y are at top left of the sprite, if via cs6 you better take the o with cross from center to top left for having a common ground of reference. Centered x y references are good for 3d specially when rotation needed.
A sprite has width and height properties also.
Lets assume you have sprites c (character) and b (block to bump)
Given:
left is left x of an object.
right is right x of an object.
top is top y of an object.
bottom is bottom y of an object.
I know this is a not very optimal function but I am writing it for demonstration purpose.
function bumpTest(c: Sprite, b: Sprite):Object {
var
r: * = {},
cLeft : int = c.x, // doing this for demonstrating you can directly use c.x
cRight : int = c.x + c.width,
cTop : int = c.y, // same
cBottom : int = c.y + c.height,
bLeft : int = b.x, // same
bRight : int = b.x + b.width,
bTop : int = b.y, // same
bBottom : int = b.y + b.height;
r.left = Boolean ((cLeft >= bLeft) && (cLeft <= bRight));
r.right = Boolean ((cRight >= bLeft) && (cRight <= bRight));
r.top = Boolean ((cTop >= bTop) && (cTop <= bBottom));
r.bottom = Boolean ((cBottom >= bTop) && (cBottom <= bBottom));
r.bump = Boolean ((r.left || r.right)&&(r.top || r.bottom));
return(r);
}
Now r has the complete answer, If r.bump is false the other values in r are irrelevant.
If r.bump turns to be true, then you can check via r.left etc from where c bumps into b. since:
r.left true means c left is into or at boundary of b.
r.right true means c right is into or at boundary of b.
r.top true means c top is into or at boundary of b.
r.bottom true means c bottom is into or at boundary of b.
Related
I want to make a tracing game. I want my circle to follow the path as the user traces the letter (path of the letter). The user can not go back to the area which is already traced
import flash.events.Event;
import flash.geom.Point;
var i: Number;
var size: int = 80;
var down: Boolean = false;
var up: Boolean = true;
var inside: Boolean = true;
var outside: Boolean = true;
var circle: Shape = new Shape();
stage.addEventListener(Event.ENTER_FRAME, loop);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseup);
char.addEventListener(MouseEvent.MOUSE_DOWN, mousedown);
function loop(e: Event) {
if (down == true) {
// Checks if mouse pointer is on path i.e 'S' alphabet
if (s.hitTestPoint(stage.mouseX, stage.mouseY, true)) {
inside = true;
outside = true;
var point: Point = maskobj.globalToLocal(new Point(stage.mouseX, stage.mouseY));
var point2: Point = new Point();
//Checks if mouse pointer is completely outside of drawn area
for (i = 0; i < 2 * Math.PI; i += (2 * Math.PI) / 10) {
point2.x = stage.mouseX + (size / 3) * Math.cos(i);
point2.y = stage.mouseY + (size / 3) * Math.sin(i);
if ((maskobj.hitTestPoint(point2.x, point2.y, true))) {
outside = false;
break;
}
}
//Checks if mouse pointer is completely inside drawn area
for (i = 0; i < 2 * Math.PI; i += (2 * Math.PI) / 10) {
point2.x = stage.mouseX + (size / 3) * Math.cos(i);
point2.y = stage.mouseY + (size / 3) * Math.sin(i);
if (!(maskobj.hitTestPoint(point2.x, point2.y, true))) {
inside = false;
break;
}
}
//Character will be moved only if mouse position not to far from current position
if (outside == false) {
if (inside == false) {
//Increases drawn area by drawing a circle shape in 'maskobj' MovieClip
circle.graphics.beginFill(0x0000ff);
circle.graphics.drawCircle(point.x, point.y, size);
circle.graphics.endFill();
maskobj.addChild(circle);
//Moves character to new position
char.x = stage.mouseX;
char.y = stage.mouseY;
}
}
}
}
}
function mouseup(e: MouseEvent): void {
up = true;
down = false;
}
function mousedown(e: MouseEvent): void {
down = true;
up = false;
}
When I trace the path,the motion is not smooth. Can someone please suggest a way to make the motion smooth OR suggest another way to achieve the same. Thank you in advance.
I've created a drawing game before that allowed the user to draw a path.
Not sure why Wicked's answer was down-voted, as the first thing you need to do is to use the highest frame rate that you can get away with. The higher the frame rate, the smoother your curve.
I see that your code draws a circle at the current position if the conditions are met. It might be better to draw a line from the last point.x/point.y to the current one instead of just a circle, so that you don't have any holes in your path.
I couldn't get around the fact that the line was jagged (a series of straight lines) as it was being drawn, but as soon as the user lifted their finger I was able to take the points along the line they had drawn and replace them with a smooth bezier Path (a series of simple bezier curves), which worked well. You could also do this on-the-fly once you have 3 points (you need 3 points to draw a curve).
Here is a good reference on how to achieve this, with theory and code samples. See further down the page for bezier paths. You'll need to convert to AS3, but it shouldn't be difficult.
Another tip is to do as little calculation as possible within the ENTER_FRAME. You could pre-calculate the two values used by your loops (2 * Math.PI) and ((2 * Math.PI) / 10) as these are constants. You could also calculate (size/3) once at the top of the function, and especially pre-calculate the 10 values for Math.sin(i) and Math.cos(i) and store them in an Array (basically a LUT - Look Up Table) as these are the heaviest math ops you're doing.
My final tip is that your code doesn't check if the point being drawn is very close to the last point that was drawn. I would recommend you do this, and only draw a point after the mouse has moved a minimum distance (e.g. 2 pixels). Otherwise you could get the mouse sitting still in one spot and your code is drawing circle upon circle on top of itself needlessly.
Try increasing the FPS in your document to atleast double what you currently have
Modify>Document...>Frame Rate
I am making this game in AS3. It's about a ball and a pad. The user can move the pad to prevent the ball from hitting the floor. Anyways, I want the ball to change color to a randomly generated color. So I have got a mathrandom for every color value, R G B.
var r;
var g;
var b;
function getColor(){
r = Math.round(Math.random()*256);
g = Math.round(Math.random()*256);
b = Math.round(Math.random()*256);
}
But I don't know how to color the ball with RGB colors. I don't even know if it's possible.
var r;
var g;
var b;
function getColor():uint{
var resultColor:uint;
r = Math.round(Math.random()*0xFF); //0xFF equivalent to 255
g = Math.round(Math.random()*0xFF);
b = Math.round(Math.random()*0xFF);
resultColor = r<<16 | g<<8 | b;
return resultColor;
}
The Color is in 0xRRGGBB format in AS3. So we combine the 3 values using bitwise operations.
Hope this might help. Check this for further info : Bit Operations in AS3
It depends on your implementation. If you're programmatically creating your ball (such as)...
var ball:Shape = new Shape();
ball.graphics.beginFill(0xFFFFFF);
ball.graphics.drawCircle(0, 0, 10);
ball.graphics.endFill();
... then you could easily draw the updated circle by first calling ball.clear() and redrawing it with your new color value. Be aware that beginFill() uses uint values, and you can easily generate these using the same Math.random(). Here's a modified version of your function that allows you to define the low & high values.
function randomRange(low:Number=0, high:Number=1):Number {
/* Returns a random number between the low and high values given. */
return Math.floor(Math.random() * (1+high-low)) + low;
}
... at which point your beginFill() would look like this:
ball.graphics.beginFill(randomRange(0x000000, 0xFFFFFF);
Which effectively retrieves a random color between black and white.
However, if you're modifying an existing image (such as a loaded Bitmap), you want a colorTransform(). Here's one for RGB I wrote:
tintRGB(target:Object, r:Number, g:Number, b:Number, alpha:Number = 1):void {
if (target.hasOwnProperty("transform")) {
target.transform.colorTransform = new ColorTransform(0,0,0,alpha,r,g,b,0);
}
}
I am using matrix.rotate method to rotate the rectangle (box in my case).
My rotate event looks like below
public function transformObject(transformEvent:TransformEvent):void{
var numChildrn:int = _markedObjectLayer.numChildren;
var tempMatrix: Matrix = null;
var tempx:Number;
var tempy:Number;
var tempHeight:Number;
var tempWidth:Number;
for(var i:int = 0; i < numChildrn; i++){
var chld:MarkedObject = ObjectLayer.getChildAt(i)
if (chld.selected){
var height:int = (BoxObject) chld.height;
var width:int = (BoxObject) chld.width;
tempMatrix = chld.transform.matrix;
tempHeight=height;
tempWidth=width;
tempMatrix = MatrixTransformer.transform(tempMatrix,transformEvent.angle);
tempMatrix.tx=tempx;
tempMatrix.ty=tempy
chld.transform.matrix = tempMatrix;
}
}
invalidateDisplayList();
}
}
The Matrix.transform method calls matrix.rotate method
public static function transform(sourceMatrix:Matrix,
rotation:Number=0 ):Matrix
{
sourceMatrix = MatrixTransformer.rotate(sourceMatrix, rotation, "degrees");
return sourceMatrix;
}
/**
* Rotates a matrix and returns the result. The unit parameter lets the user specify "degrees",
* "gradients", or "radians".
*/
public static function rotate(sourceMatrix:Matrix, angle:Number, unit:String = "radians"):Matrix {
if (unit == "degrees")
{
angle = Math.PI * 2 *( angle / 360);
}
sourceMatrix. rotate(angle)
return sourceMatrix;
}
The issue is that x and y are left corener of the box and hence it is rotating around left corner. However, if I try to give temp.x and temp.y as centroid value it does not rotate around centroid?
Can any one suggest what am I doing wrong here?
Thanks
Akshay
If you really want or have to use matrices directly, you can do this more conveniently using a built-in flash class: fl.motion.MatrixTransformer:
MatrixTransformer.rotateAroundInternalPoint(matrix, centroidX, centroidY, angleInDegrees);
See the Adobe docs on MatrixTransformer for more information.
However, if you don't need to use the transformation matrix, the simpler solution would be to:
have your objects drawn in such a way that (0, 0) is their centroid
use the simple rotation property from DisplayObject which achieves the same goal in a much simpler manner
Figured it out. It appears that I was not translating them to appropriate coordinate positions before and after rotation
//Step 1 Fix the co-ordinates of rectangle . I added them to an event so that they remain static
if (TransformEvent.X == 0 && TransformEvent.Y == 0)
{
TransformEvent.X = chld.x;
TransformEvent.Y = chld.y;
}
//next get the centroid of rectangle
tempx = TransformEvent.X + width/2;
tempy= TransformEvent.Y +height/2;
// Step 3: translate before rotation
tempMatrix.translate(-1*tempx,-1*tempy);
//Rotate the rectangle
tempMatrix = MatrixTransformer.transform(tempMatrix,transformEvent.angle);
//translate to centroid after rotation
tempMatrix.translate(tempx,tempy);
//assign back the matrix to the rectangle
chld.transform.matrix = tempMatrix;
Thanks for all your help. Also the this site helped me with translation bit
http://www.foxarc.com/blog/article/66.htm
i try to script a flow-animation ( basic color, nothing fancy ) through a grid of "pipes".
( think of a 5*5 tiled screen)
since the pipes are created completely dynamic at runtime, the animation has to be scripted also.
At the moment it escapes my mind on how to do this in actionscript, without pre-generated masks.
thanks for all hints!
You wish to visually 'simulate' the flow of some liquid (for instance water) through pipes in the same style that it is done in pipe games?
http://www.mclelun.com/img/blog/120411_pipe_02.jpg
Alright...
Are you willing to use bitmapData (pixel) to create this effect?
Here is how I would go about it..
create a short script to fill a rectangle (block) of pixels gradually..
ie
var animateOn : Boolean = true;
var startPoint : Point = new Point(beginX , beginY);
var endPoint : Point = new Point(finishX, finishY);
var step : Number = 1 / Point.Distance(startPoint, endPoint);
var currentPos : Number = 0;
onEnterFrame(e : Event):void
{
var p : point = Point.interpolate(startPoint, endPoint, currentPos);
bitmap.drawRect(p.x - 2, p.y - 2, 4, 4, someColor);
currentPos += step;
}
This is just an example of the top of my head (will not compile)
The idea is to keep feeding in the right startPoint and endPoint for each tile..
You could easily animate it like with without using any mask.
You can define each type of tile as a vector of points and then iterate from one point to another by adding the position of the tile to each point.
To fill a curve dynamically you may want to use this closed formula for a Bezier curve:
//start point
var s = new Point(x0, y0);
//cont point
var c = new Point(x1, y1);
//end point
var e = new Point(x2, y2);
var step : Number = 1 / (Point.distance(startPoint, controlPoint) + Point.distance(controlPoint, endPoint));
var t : Number = 0.0;
private function onEnterFrame(e : Event):void
{
var p : Point = new Point();
p.x = (s.x * (1-t) + c.x * t) * (1 - t) + (c.x * (1-t) + e.x * t) * t;
//do the same for y axis
drawSomething(p.x, p.y);
t+= step;
}
this will animate a curve style flow
I am making a Sim City like game. There are lots of tiles. When I first started. I was just using a tilesheet. I was copying the necessary pieaces from the tilesheet. on to a blank bitMapData. I then took the bitMapData and put it into a bitMap which I then put into a DisplayObject. It worked great!
tileSheet:BitMapData <----- data is already in
loop { loop through and tiled
bg:bitMapData= new bitMapData();
bg.copyPixel(tileSheet,rect,point);
}
canvas.BitMap(bg);
addChild(canvas);
Only problem was I needed to make my tiles interactive. I needed to highlight them and change colors and stuff. So I used the Sprite object. It works great but I can only have so many on the stage at once. or else it moves slow when I scroll. I need something Lighter then a sprite, but yet I can still turn into a object to make interactive. Anyone have any ideas ???
If you have a lot of tiles, that will impact performance because Flash needs to update the transformations of a lot of display objects (which internally means a lot of matrix calculations, and subsequent redraws of big areas of the screen.)
There is another way to achieve interactivity, if you find that you must use a single bitmap data for performance. Keep an "abstract" (i.e. not graphical) data model in memory, that stores your game state. Make sure that you are able to read from your store where a certain element is positioned in the game world. Then you can use a flat bitmap data to render the game world, because the individual positions are stored elsewhere.
When the user clicks the DisplayObject containing the bitmap data (a Sprite in which the bitmap is drawn using a bitmap fill, or that wraps a Bitmap), look in your model which of your game elements was hit by that click.
// myTileSprite is a Sprite with a bitmap fill
myTileSprite.addEventListener(MouseEvent.CLICK, handleWorldClick);
function handleWorldClick(ev : MouseEvent) : void
{
var i : int;
// Loop through all game element data models
for (i=0; i<myGameElements.length; i++) {
// Test the mouse position against the element model
if (myGameElements[i].hitTest(myTileSprite.mouseX, myTileSprite.mouseY)) {
trace('this was the element that was clicked: '+myGameElements[i].toString());
}
}
}
Here, whenever the player clicks the world graphics, the loop tries to find that element which was directly under the mouse position. You will need to implement a hitTest() method on all your game element data models, of course. Such a method simply checks the supplied world space position against the tile's area:
// GameElement.hitTest():
/**
* Tests a world position against the position and area of this game
* element tile. Returns a boolean indicating whether this tile was hit.
*/
public function hitTest(mouseX : Number, mouseY : Number) : void
{
var rect : Rectangle = new Rectangle(this.worldX, this.worldY, this.width, this.height);
if (mouseX > rect.left && mouseX < rect.right
&& mouseY > rect.top && mouseY < rect.top) {
return true;
}
else return false;
}
The GameElement class is not an display object, but has worldX and worldY properties indicating where it is located in the world. It's width and height properties define it's dimensions.
The trick from hereon is to make sure that the rendered bitmap and your model storage is synchronized, so that a tile's position on the bitmap really corresponds to it's worldX/worldY properties in the data model.
I am one step ahead of you. And that is a great idea. Its alot easier to keep a data representation of the world when the tiles are squared. I therefore can take my mouseX/tileWidth, and thats hw many columns I moved from left to right. same with the Y axis.
Not only that but coordinates start at top left corner.
But issue I have is that my tiles are Isometric. So instead of the X axis start off like...
012345678
0
1
2
3
4
5
6
7
8
My tiles are aligned like...
00
1 1
2 2
3 3
4 4
5 6
its a little sloppy. but the right side represents the y axis and the left represents the x axis. and the center origin is in the center of the screen. not on the top left. I am trying to figure out how to measure where my mouse is from the center and out on both sides. This sounds extremely difficult. I am not sure if its possible. The game is suppose to be like a sim city like game. The first sim city was squares not isometric. I dont think they went isometric until they started using 3d. I wonder if its possible to create a illusion of isometric on a square tile.
Ive been reading this great book on isometrics. They show to calculate tiles in 3d space. and even calculate your mouse in 3d space as well. here is the code. Its alot, but I hope someone else understands it more then I. The book was written by jobe makar on building multiplayer worlds. I wanted to share it because the code it is pretty simple as far as amount of code put into it. only 2 classes needed. I am not that good with trigonometry. so I cant really interpret how the math is getting the results. hopefully someone can explain that for me :D.
Y coordinates are not given because the width is = to height. The coordinates method is just a custom made Point class which holds x, y and z.
package com.gamebook.grid {
import com.gamebook.utils.geom.Coordinate;
import com.gamebook.utils.Isometric;
import flash.display.MovieClip;
import flash.events.MouseEvent;
/**
* ...
* #author Jobe Makar - jobe#electrotank.com
*/
public class Map extends MovieClip{
private var _grid:Array;
private var _iso:Isometric;
private var _tileWidthOnScreen:int;
private var _tileHeightOnScreen:int;
private var _tileWidth:Number;
private var _tileHeight:Number;
private var _cols:int;
private var _rows:int;
private var _lastTile:Tile;
public function Map() {
initialize();
}
private function initialize():void{
_iso = new Isometric();
//when mapped to the screen the tile makes a diamond of these dimensions
_tileWidthOnScreen = 64;
_tileHeightOnScreen = 32;
//figure out the width of the tile in 3D space
_tileWidth = _iso.mapToIsoWorld(64, 0).x;
//the tile is a square in 3D space so the height matches the width
_tileHeight = _tileWidth;
buildGrid();
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoved);
}
private function mouseMoved(e:MouseEvent):void {
if (_lastTile != null) {
_lastTile.alpha = 1;
_lastTile = null;
}
var coord:Coordinate = _iso.mapToIsoWorld(mouseX, mouseY);
var col:int = Math.floor(coord.x / _tileWidth);
var row:int = Math.floor(Math.abs(coord.z / _tileHeight));
if (col < _cols && row < _rows) {
var tile:Tile = getTile(col, row);
tile.alpha = .5;
_lastTile = tile;
}
}
private function buildGrid():void{
_grid = [];
_cols = 10;
_rows = 10;
for (var i:int = 0; i < _cols;++i) {
_grid[i] = [];
for (var j:int = 0; j < _rows;++j) {
var t:Tile = new Tile();
var tx:Number = i * _tileWidth;
var tz:Number = -j * _tileHeight;
var coord:Coordinate = _iso.mapToScreen(tx, 0, tz);
t.x = coord.x;
t.y = coord.y;
_grid[i][j] = t;
addChild(t);
}
}
}
private function getTile(col:int, row:int):Tile {
return _grid[col][row];
}
}
}
Then we have the isometric class that calculates 3d space.
package com.gamebook.utils {
import com.gamebook.utils.geom.Coordinate;
/**
* #author Jobe Makar - jobe#electrotank.com
*/
public class Isometric {
//trigonometric values stored for later use
private var _sinTheta:Number;
private var _cosTheta:Number;
private var _sinAlpha:Number;
private var _cosAlpha:Number;
/**
* Isometric class contrustor.
* #param declination value. Defaults to the most common value, which is 30.
*/
public function Isometric() {
var theta:Number = 30;//even though the tiles are already isometric, you still have to put the degrees the tiles will be turned.
var alpha:Number = 45;//45 degrees on y axis, 30 dgrees on x axis
theta *= Math.PI/180; // then you translate to radians
alpha *= Math.PI/180;
_sinTheta = Math.sin(theta);
_cosTheta = Math.cos(theta);
_sinAlpha = Math.sin(alpha);
_cosAlpha = Math.cos(alpha);
}
/**
* Maps 3D coordinates to the 2D screen
* #param x coordinate
* #param y coordinate
* #param z coordinate
* #return Coordinate instance containig screen x and screen y
*/
public function mapToScreen(xpp:Number, ypp:Number, zpp:Number):Coordinate {
var yp:Number = ypp;
var xp:Number = xpp*_cosAlpha+zpp*_sinAlpha;
var zp:Number = zpp*_cosAlpha-xpp*_sinAlpha;
var x:Number = xp;
var y:Number = yp*_cosTheta-zp*_sinTheta;
return new Coordinate(x, y, 0);
}
/**
* Maps 2D screen coordinates into 3D coordinates. It is assumed that the target 3D y coordinate is 0.
* #param screen x coordinate
* #param screen y coordinate
* #return Coordinate instance containig 3D x, y, and z
*/
public function mapToIsoWorld(screenX:Number, screenY:Number):Coordinate {
var z:Number = (screenX/_cosAlpha-screenY/(_sinAlpha*_sinTheta))*(1/(_cosAlpha/_sinAlpha+_sinAlpha/_cosAlpha));
var x:Number = (1/_cosAlpha)*(screenX-z*_sinAlpha);
return new Coordinate(x, 0, z);
}
}
}