I'm trying to make a pure AS3 game, and need a way to split the screen so that two players can have individual "cameras" that follow the around the game world. The problem is that a sprite can't have multiple parents. I'm trying to hack my way around this problem by having classes that duplicate sprites and manage all of their updates, but I'm not getting very far and my code is getting very, very ugly.
Does anyone know a good workaround or method for doing this? I can't seem to find much on-line on the subject.
I think you should use BitmapData copyPixels method
.copyPixels(point_0, rectangle_0)---> FirstPlayerScreen
World.Bitmap -
.copyPixels(point_0, rectangle_0)---> SecondPlayerScreen
Thanks for everyone's suggestions. What I've ended up with is a World class that I can add regular Sprite objects to as children. The World object manages and updates copies of those sprites, and world.getCamera() can be called as many times as necessary to display custom copies of the game world.
The key part is making copies of the sprites, this is the function I wrote to do that:
public function bitmapCopy(original:DisplayObject):Sprite
{
var returnSprite:Sprite = new Sprite();
if (original.width == 0 || original.height == 0) return returnSprite
var x = original.x; var y = original.y
var rotation = original.rotation
original.x = 0; original.y = 0; original.rotation = 0
var bounds:Rectangle = original.getBounds(original.parent)
var m:Matrix = new Matrix()
m.tx = -bounds.x
m.ty = -bounds.y
var bitmapData:BitmapData = new BitmapData( bounds.width, bounds.height, true,
0xFF0000 );
bitmapData.draw(original, m);
var bitmap:Bitmap = new Bitmap( bitmapData );
bitmap.x = bounds.x
bitmap.y = bounds.y
returnSprite.addChild(bitmap);
returnSprite.x = original.x = position.x
returnSprite.y = original.y = position.y
returnSprite.rotation = original.rotation = rotation
return returnSprite;
}
The returned Sprite can be added to the stage and will act in exactly the same way as the original (except for being static, of course). Hopefully this should help anyone else who comes across this problem.
Your theory is on the right track - you're probably just stumbling over your implementation. Organization and keeping things object-oriented will be your best friend in this scenario.
It's hard to give you a good example based off of a limited description of your game, but in general, I'd have a Screen class that can be instantiated multiple times, and I'd keep track of each instantiation (that gives us our multiple "players"). Each Screen has a stage, and you're building your game world on that stage.
The key here is data organization and good communication. Remember, there's only one "world", and you're just showing multiple instances of it. So you'll want a central Model to store data about every object in your singular game world. That Model will drive the rendering of that world to your multiple screens. If a player changes an object on their screen (let's say they move it), then you'll update the Model with that object's new location. Then, you'll broadcast those new coordinates to each Screen instance, so that all of your screens will update.
How you "broadcast" this can vary (and depends largely on the real-time nature of your game). If your game is very real-time and you're running a game loop, then you may just want to pass the objects' data along in every loop, and they'd update that way. If your game isn't as dependent of being real-time, then you can set up event listeners or a custom notification system that'll alert all of the instances of an object to update itself.
I know this is very high-level, but it's hard to give an in-depth answer without more info about your game. Hopefully this helps point you in the right direction - what you're attempting is definitely not simple, so don't get discouraged!
If you develop your game using MVC architecture principles then it should be trivial to draw your game twice, each player having an instance of the game's "view" but positioned according to a different character. If you give a layer mask to each instance then you could put the two instances side by side and so create a split screen effect.
Happy coding!
I'm working on a split screen myself just now:
masking to draw 1 world in 2 branches of your DisplayObject tree (separated cleanly), copying pixels for a split screen is a bad idea
data object to describe the world (not Sprite or DisplayObject)
Works very well so far. I've got 1 level, 2 players who move independently from each other and 2 screens that follow one player at a time. I see the other player in one player's screen and the one player in the other player's screen.
Here is how I did the data object part:
Define a central data object which describes the world and all it's world objects.
Write: make some sprites being able to manipulate 1 object of the world.
Read: update sprites by checking the properties of the world objects. 2 Screens -> 2 sprites for every world object. Check them every frame or try events.
Related
I´m programing a space ship side scroll in as3. The bottom of the stage are mountains and here comes the problem, when I try to detect the ship collision against the mountains..
Because the poor collision detection and the need of avoid large loops my idea is create an object that works as a collider itself detecting a collision and avoiding parse all the stage or more selective metod.
I place "by hand" in the flash stage several instances of circles with a class for manage them where I place the If(this.collider.hits(ship)....
I spent looong time but I can find the way to make it work some of the mistakes i get are like this
Error 1061: Call to a possibly undefined method hitTestObject through a reference with static type Class.
some Idea? Thanks in advance
when you hit test with points it is important that the point being tested is relative to the object being tested against, eg
if(mountain.hitTestPoint(this.x + circle1.x, this.y + circle1.y))
will return true if the circles are inside the object calling the function because their position relative to the mountain is now relative to it rather then relative to the ships xy position within the clip... hope that makes sense.
btw I have done this myself in the past but I would have to remind you that you can only hit test with the points so there is no need to have circles, use blank sprites instead and set the visible flag in the properties panel to false, no drawing will make it slightly faster... not that you will notice, also sprites/graphics use less memory then movie clips.
also I would recommend hard coding some points in the clips rather then actually adding the clips in the sprite/clip itself, this will make it easier to work with them and scale later on (believe me this will annoy the hair from your head to do something later and slow the game to scale on the fly)
try something like this... you can determine the points values by adding a clip to the movie clip and getting its position from the properties if you must.
private var hitPoints:Vector.<Point> = new Vector.<Point>
hitPoints.push(new Point(10, 40));
hitPoints.push(new Point(30, 40));
//...do this for all your points
//loop through all your points and check if the hit relative to the ships position.
for(var i:int = 0; i < hitPoints.length; i++)
{
if (scene.hitTestPoint(ship.x + hitPoints[i].x, ship.y + hitPoints[i].y))
{
//do your hit stuff here
break;//don't forget to break
}
}
in this code you will need to make sure the scene object is a reference to your scenery at the bottom of the screen.
I hope this helps but if this is not enough help then you should post some of your code here so we can have a look and see where it can be improved.
I'm new at ActionScript 3.0 so if you guys can help me a little.
I want to make an obstacle which block a path to player. I made this like that that I'm saving all movments to array and than if they collide it moves player to previous position. Is there another way because I think this is not the proper way to do it. And sometimes when it collides player is unable to move. Can you give me an example :)
Thanks
This is the only way you can ever detect a collision, but in a bit more refined way.
You actually collide the bodies (but do not apply the change to the actual object, yet).
Check for all colliding bodies on stage.
Take necessary step (roll back, destroy.. anything)
Apply the change & Render the bodies, on screen.
Considering the above as an example for flash :
var hero:Sprite = new Sprite();
addChild(hero);
while(1) {
var newX = hero.x + 1;
if(newX < 100)
hero.x = newX;
}
Every game should have a loop. The loop must branch out to various situations. So that's your start.
The hero object probably moves with the user interaction & the checks keep increasing, compelling you to re think the solution as your project grows more & more dense...
I´m making a level creator for my platformer game on AS3. I´m looking for a way of getting a graphical representation of a library object on my .fla, so the user can insert that object on the world on the desired coordinate. What I mean with this is that I just want a picture of the first frame of the mc, without taking its properties and methods (without the need to make a .jpg containing that image), because that´s when problems begin to appear, and I just want the object functionality on play mode, not the level creator.
Let´s say, for example, a ball that bounces all the time by updating its location every frame. On the level creator I just want to get the ball picture to insert it on the desired location. If I take the whole ball object to the level creator it won´t stop bouncing and it will mess things up.
I hope I´m clear... I just want to know if there is a practical solution to this; if not then I´ll make a class where all the world objects would extend to, that has a init() function that initializes all the object functionality, so it´s called only on play mode and not on level creator. Thanks for reading anyway.
What you're doing wrong is adding functionality directly to the MovieClip.
Please read one of my previous answers that covers this concept in more depth.
MovieClips should be used for the display only, representing the graphics of an actual game class, for example:
public class Player
{
// Graphics that represent the Player.
protected var display:MovieClip;
// Constructor.
public function Player()
{
display = new PlayerMovieClip();
}
}
Anyways, once you fix that you can take a Bitmap sample of MovieClips using BitmapData and its draw():
// Assume that 'mc' is your MovieClip.
var sample:BitmapData = new BitmapData(mc.width, mc.height, true, 0);
sample.draw(mc);
// This is your image.
var texture:Bitmap = new Bitmap(sample);
I'm trying to make a shape available for duplicating. Here's an explanation of what I've done, what I'm trying to do, and where I'm stuck:
Drew a shape manually in the Flash IDE (paintbrush).
Created a new movieclip containing the shape; exports as a class.
Instantiate the class (var mc:MovieClip = new shapeMovieClip()).
Add a reference to the shape in mc that I want (var myShape:Shape = mc.getChildAt(0) as Shape;
This works perfect and I now have my shape, but how can I duplicate it without instantiating the parent MovieClip class - is it possible?
I've tried creating a new shape and using the copyFrom() graphics method with no avail, I believe this method just copies draw calls when they're made on the referenced graphics and instead I just have a reference of a shape that's already been drawn.
Right now I'm extending the movieclip as a sprite instead, instantiating, pulling the shape from the parent and saving its reference, and then nulling the sprite. Does anyone know of a better, more lightweight, strategy for duplicating Shapes in this manner?
Basically depends on whether you need to scale your shapes. If you don't, and you can work it out with a fixed sized bitmap representation of the shape, then you will get much better performance drawing your shape to a BitmapData (it's called rasterisation) and instanciating Bitmap objects (as other commenters have pointed out). The code would go something like this:
var base:Sprite = new shapeMovieClip();
var bmd:BitmapData = new BitmapData(base.width, base.height, true, 0);
bmd.draw(base);
var clip1:Bitmap = new Bitmap(bmd);
var clip2:Bitmap = new Bitmap(bmd);
If you do need to scale the clips, you will get pixelation using bitmaps. When scaling down Bitmap.smoothing can help to some extent (also when rotating), but if you need to scale up, you will probably have to use some kind of mip-mapping. This is basically creating a few bitmaps of the shape at different scale levels, and then swap them depending on the current scale. Coding this has some complexity (using a helper parent to adjust the scale can help) but it will definitely perform better than using many shape symbols (with or without a sprite parent).
This is very old, but it still comes up high in Google, so I just wanted to share a true shape duplicating method:
var shapeonstage:Shape = shapeMadeInIDE;
var g:Vector.<IGraphicsData> = shapeonstage.graphics.readGraphicsData();
var shapecopy:Shape = new Shape();
shapecopy.graphics.drawGraphicsData(g)
And boom. It works. Had to share this because it would have helped me a looooong time ago and in so many ways.
UPDATE:
There is some clarification I'd like to add why you would want to use this method, for duplicating AND for storing references to Shapes that are within an swf.
If you target a Shape object on the stage or in a movie clip, the Flash Rendering engine does something strange. It will RECYCLE Shape objects to render new graphics thus making your Shape reference point to a COMPLETELY different rendering.
For example:
Create an fla with a movieclip.
Inside the movie clip make 10 frames.
On each frame, draw a different shape
In your code, get a reference to the shape (The Shape on Frame 1)
Draw and verify your shape (draw to a bitmap then put the bmp on stage)
Now let the flash engine play for 5 frames
Draw and verify your shape again
The second time you draw your shape without EVER reassigning your shape reference, will SOMETIMES yield a completely different shape.
Just know, this little quirk can leave you pulling your hair out if you don't know what you're looking for :)
I know that there are people out there creating classes for this (ie http://coreyoneil.com/portfolio/index.php?project=5). But I want to learn how to do it myself so I can create everything I need the way I need.
I've read about BitMap and BitMapData. I should be able to .draw the MovieClips onto a BitMap so I could then cycle the pixels looking for the collisions. However, It's weird and confusing dealing with the offsets.. And it seams like the MyBitMap.rect has always x = 0 and y = 0... and I can't seam to find the original position of the things...
I'm thinking of doing a hitTestObject first, then if this was positive, I would investigate the intersection betwen the movieclips rectangles for the pixel collisions.
But then there is also another problem (the rotation of movieclips)...
...I need some enlightment here on how to do it.
Please, any help would be appreciated..
If you're using BitmapData objects with transparency you can use BitmapData.hitTest(firstPoint:Point, firstAlphaThreshold:uint, secondObject:Object, secondBitmapDataPoint:Point = null, secondAlphaThreshold:uint = 1):Boolean.
You'll have to change from global coords to the local BitmapData coords which will require a bit of math if it is rotated. That's easily achieved (look up affine transform for more info on wiki):
var coordTransform:Matrix = new Matrix();
coordTransform.rotate(rotationRadians);
coordTransform.translate(x, y);
coordTransform.transformPoint(/* your point */);
A classic reference for pixel perfect collision detection in flash is this Grant Skinner's article. It's AS2, but the logic is the same for AS3 (there are ports available if you google a bit).
If I recall correctly, this particular implementation worked as long as both tested objects had the same parent, but that can be fixed.
About BitmapData x and y values, I understand it could be confusing; however, the way it works makes sense to me. A BitmapData is just what the name implies: pixel data. It's not a display object, and cannot be in the display list; so having x or y different than 0 doesn't really make sense, if you think about it. The easiest way to deal with this is probably storing the (x,y) offset of the source object (the display object you have drawn from) and translate it to the global coordinate space so you can compare any objects, no matter what's their position in the display list (using something like var globalPoint:Point = source.parent.localToGlobal(new Point(source.x,source.y)).
I've previously used Troy Gilbert's pixel perfect collision detection class (adapted from Andre Michelle, Grant Skinner and Boulevart) which works really well (handles rotation, different parents, etc.):
http://troygilbert.com/2007/06/pixel-perfect-collision-detection-in-actionscript3/
http://troygilbert.com/2009/08/pixel-perfect-collision-detection-revisited/
and from there he has also linked to this project (which I've not used, but looks really impressive):
http://www.coreyoneil.com/portfolio/index.php?project=5
I managed to do it after all, and I already wrote my class for collision detections,/collisions angle and other extras.
The most confusing process is maybe to align the bitmaps correctly for comparing. When whe draw() a movieclip into a a BitmapData, if we addChild() the corresponding Bitmap we can see that part of it is not visible. it appears to be drawn from the center to right and down only, leaving the top and left parts away from beeing drawn. The solution is giving a transform matrix in the second argument of the draw method that aligns the bitmap and makes it all be drawn.
this is an example of a function in my class to create a bitmap for comparing:
static public function createAlignedBitmap(mc: MovieClip, mc_rect: Rectangle): BitmapData{
var mc_offset: Matrix;
var mc_bmd: BitmapData;
mc_offset = mc.transform.matrix;
mc_offset.tx = mc.x - mc_rect.x;
mc_offset.ty = mc.y - mc_rect.y;
mc_bmd = new BitmapData(mc_rect.width, mc_rect.height, true, 0);
mc_bmd.draw(mc, mc_offset);
return mc_bmd;
}
in order to use it, if you are on the timeline, you do:
className.createAlignedBitmap(myMovieClip, myMovieClip.getBounds(this))
Notice the use of getBounds which return the rectangle in which the movie clip is embedded. This allows the calculation of the offset matrix.
This method is quite similar to the on shown here http://www.mikechambers.com/blog/2009/06/24/using-bitmapdata-hittest-for-collision-detection/
By the ways, if this is an interesting matter for you, check my other question which I'll post in a few moments.