I'm creating a simple drawing prototype to be used on Android where the user can drag his finger across the screen and draw basic lines/shapes etc. I'm having some performance issues when drawing over the same areas and after a while performance drops considerably.
I am wondering if there is any way to, after the line has been drawn (after a touch begin, touch move, and touch end event chain), to store the newly drawn line into a bitmap containing the rest of the drawings.
I've had a look at bitmap.merge() but this would create problems when it came to mixing colors. I simply want any new 'drawings' to be saved on top of everything drawn previously.
// To hold current 'drawing'
var clip:Shape = new Shape();
// To hold past 'drawings'
var drawing:Bitmap = new Bitmap();
public function Main()
{
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
addChild(drawing);
addChild (clip);
addEventListener(TouchEvent.TOUCH_BEGIN, tBegin);
addEventListener(TouchEvent.TOUCH_MOVE, tMove);
addEventListener(TouchEvent.TOUCH_END, tEnd);
}
private function tBegin(e:TouchEvent):void
{
clip.graphics.lineStyle(28,0x000000);
clip.graphics.moveTo(mouseX, mouseY);
}
private function tMove(e:TouchEvent):void
{
clip.graphics.lineTo(mouseX, mouseY);
}
private function tEnd(e:TouchEvent):void
{
// Save new graphics and merge with drawing
}
Just keep drawing in your clip shape, and on tEnd draw clip inside a bitmapData assigned to a bitmap
// To hold current 'drawing'
var bmpData:BitmapData = new BitmapData (800, 800) // put here your desired size
var clip:Shape = new Shape();
// To hold past 'drawings'
var drawing:Bitmap = new Bitmap(bmpData);
public function Main()
{
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
addChild(drawing);
addChild (clip);
addEventListener(TouchEvent.TOUCH_BEGIN, tBegin);
addEventListener(TouchEvent.TOUCH_MOVE, tMove);
addEventListener(TouchEvent.TOUCH_END, tEnd);
}
private function tBegin(e:TouchEvent):void
{
clip.graphics.lineStyle(28,0x000000);
clip.graphics.moveTo(mouseX, mouseY);
}
private function tMove(e:TouchEvent):void
{
clip.graphics.lineTo(mouseX, mouseY);
}
private function tEnd(e:TouchEvent):void
{
// Save new graphics and merge with drawing
bmpData.draw (clip);
clip.graphics.clear();
}
Related
i am new to unity and need some help regarding creating a background that will look something like this (A bit jittery because its a gif), i want it to be like fill every screen size and have size 1/8th of the screen (the black box):
You can use the following setup:
First the image should have borders like this one and set its Wrap mode to reapeat in the import settings
Your background should be a ScreenSpace Overlay Canvas (depends on your setup ofcourse)
Within that Canvas have a RawImage object, use your image as Texture and add this component to it
[RequireComponent(typeof(RawImage))]
public class BackgroundController : MonoBehaviour
{
[Header("References")]
[SerializeField] private RectTransform _rectTransform;
[SerializeField] private RectTransform _parentRectTransform;
[SerializeField] private RawImage _image;
[Header("Settings")]
[SerializeField] private Vector2 repeatCount;
[SerializeField] private Vector2 scroll;
[SerializeField] private Vector2 offset;
private void Awake()
{
if (!_image) _image = GetComponent<RawImage>();
_image.uvRect = new Rect(offset, repeatCount);
}
// Start is called before the first frame update
private void Start()
{
if (!_rectTransform) _rectTransform = GetComponent<RectTransform>();
if (!_parentRectTransform) _parentRectTransform = GetComponentInParent<RectTransform>();
SetScale();
}
// Update is called once per frame
private void Update()
{
#if UNITY_EDITOR
// Only done in the Unity editor since later it is unlikely that your screensize changes
SetScale();
#endif
offset += scroll * Time.deltaTime;
_image.uvRect = new Rect(offset, repeatCount);
}
private void SetScale()
{
// get the diagonal size of the screen since the parent is the Canvas with
// ScreenSpace overlay it is always fiting the screensize
var parentCorners = new Vector3[4];
_parentRectTransform.GetLocalCorners(parentCorners);
var diagonal = Vector3.Distance(parentCorners[0], parentCorners[2]);
// set width and height to at least the diagonal
_rectTransform.sizeDelta = new Vector2(diagonal, diagonal);
}
}
This first scales the RawImage to fit the diagonal size of the parent. Since it is already fitting the screen this gets us the screen sizes => always fills the entire screen, no matter what the scales or rotation are (as long as your RawImage is on the center of the screen ofcourse).
Using the repeatCount you define how often the texture should be on the background.
Then using the scroll you can define how fast and in which direction the background should scroll. The script basically simply updates the RawImage.uvRect every frame.
Finally you simply rotate the RawImage so the scroll goes in the final direction you want
So you want some kind of infinity scrolling background?
Here is some simple way to create it.
1) You will need tilable image (that can be connected one side to other seamlessly). You can use one frame from your gif. Add it to your assets (just drag and drop it there).
2) In your Unity scene create new Quad object (GameObject->3d Object->Quad)
3) Drag and drop your image from your assets window right onto your Quad. That will apply texture to it.
4) Create simple script on your Quad object. I called mine RollerScript
using System.Collections;
using UnityEngine;
public class RollerScript : MonoBehaviour
{
public float speed = 2f;
public MeshRenderer renderer;
void Update()
{
Vector2 offset = new Vector2(Time.time * speed, Time.time * speed);
renderer.material.mainTextureOffset = offset;
}
}
5) Go back to Editor and assign renderer field (drag your Quad object from Hierarchy to that field)
6) Hit Play and adjust speed parameter in your script editor window. Your texture will scroll diagonally to right-top (as on your gif). If you want another direction you can change this line:
Vector2 offset = new Vector2(Time.time * speed, Time.time * speed);
Set x or y value of Vector2 to zero if you want NO scrolling horizontaly/vertically. Change x or y value to -x or -y if you want to scroll in opposite direction.
Hey guys so I've been trying to solve this for awhile I have looked on many forums and tried to understand what they were trying to convey with the code using ActionScript 3 but i still get nothing. My main goal is to have a character on stage named "mainPlayer" now i want to set up touch events so that when the user drages his finger up down or side to side i want to the mainPlayer to follow the users path or if the user touches a point on the screen and holds his finger there the mainPlayer will be attracted to the touch and move to the point where the finger is currently at on the screen.
Ive seen lots of stuff with Phase and ID implemented but dont really understand whats going on
so far this is what i have set up:
public class realmEngine extends MovieClip
{
//MultiTouch gestures
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
Multitouch.inputMode = MultitouchInputMode.GESTURE;
public var mainPlayer:mcPlayer;
//PlayerControls
private var speed:Number = 8.0;
public var vx:Number = 0;
public var vy:Number = 0;
private var friction:Number = 0.85;
private var maxSpeed:Number = 15;
public function realmEngine()
{
//Add Player to Stage
mainPlayer = new mcPlayer();
stage.addChild(mainPlayer);
mainPlayer.x = (stage.stageWidth / 2) - 300;
mainPlayer.y = (stage.stageHeight / 2 );
//trace("this works");
//Setup Touch Event listeners
mainPlayer.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
stage.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
stage.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
//Game Loop Event Listener
stage.addEventListener(Event.ENTER_FRAME, gameLoop);
}
private function gameLoop(e:Event):void
{
mainPlayerControls();
}
private function mainPlayerControls():void
{
}
private function onTouchEnd(e:TouchEvent):void
{
}
private function onTouchMove(e:TouchEvent):void
{
}
private function onTouchBegin(e:TouchEvent):void
{
}
}
I'm not sure what to do inside the onTouch Functions in order for the object that i add to stage by Code to follow the users touch on the screen.
Can anyone lead my in the right direction or give me any advice? I woudld really appreciate it thanks guys
Yes I happen to know how to do this, I just wasn't sure if I had grasped fully what you wanted to achieve.
Note that I won't be taking into account the speed and maxSpeed variables for moving the player. It's beyond this scope and beyond the scope of the top of my head. A little bit of internet searching will get you far on that subject however!
First of all, in order to make the object follow a path drawn by the user, we need a way to store the path. For this, I suggest a Vector with Point as its datatype. It's fast and easy to work with when adding and removing elements without having to worry about its length.
We also need a way to tell wether the player sprite should move or not, in other words wether the user is pressing the finger on the screen or not.
private var _pathPoints : Vector.<Point>;
private var _isMoving : Boolean = false;
Easy-cakes. Now for the fun part!
First, we need to change the scope of the onTouchBegin event, from mainPlayer to the stage. If we don't, the user won't be able to touch an abstract point on the stage and get the player sprite to move there. Simply done with a change to
mainPlayer.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
Then we take care of when the user moves his or her finger. Nothing fancy going on here.
We're just simply storing the coordinates in our vector and storing the current state of wether the user is pressing the screen or not.
private function onTouchBegin ( e:TouchEvent ) : void
{
_pathPoints.push( new Point( e.stageX, e.stageY ) );
_isMoving = true;
}
private function onTouchMove ( e:TouchEvent ) : void
{
_pathPoints.push( new Point( e.stageX, e.stageY ) );
}
private function onTouchEnd ( e:TouchEvent ) : void
{
// Dirty but quick way of clearing the vector
_pathPoints.splice(0);
_isMoving = false;
}
Finally, for the even funnier part; the main game loop! Or "Where the Magic Happens".
private function mainPlayerControls () : void
{
// Update player position and forces
vx *= friction;
vy *= friction;
mainPlayer.x += vx;
mainPlayer.y += vy;
// Check if the player should be moving to a new point
if( _isMoving )
{
// Get a reference to the current target coordinate
var target : Point = _pathPoints[0];
// Check if the player position has reached the current target point
// We use a bounding box with dimensions equal to max speed to ensure
// that the player doesn't move across the point, move back towards it
// and start jojo-ing back and forth
if(mainPlayer.x >= target.x - maxSpeed && mainPlayer.x <= target.x + maxSpeed &&
mainPlayer.y >= target.y - maxSpeed && mainPlayer.y <= target.y)
{
// The player has reached its target
//so we remove the first element of the vector
_pathPoints.shift();
// and update the target reference
target = _pathPoints[0];
}
// Calculate velocities to the first element of the vector
vx = mainPlayer.x - target.x;
vy = mainPlayer.y - target.y;
}
}
I managed to load a tmx map now I would like to create the obstacle that the sprite can not move, I recovered the obstacle like this :
try {
final TMXLoader tmxLoader = new TMXLoader(this, this.mEngine.getTextureManager(), TextureOptions.BILINEAR_PREMULTIPLYALPHA, new ITMXTilePropertiesListener() {
#Override
public void onTMXTileWithPropertiesCreated(final TMXTiledMap pTMXTiledMap, final TMXLayer pTMXLayer, final TMXTile pTMXTile, final TMXProperties<TMXTileProperty> pTMXTileProperties) {
/* We are going to count the tiles that have the property "cactus=true" set. */
if(pTMXTileProperties.containsTMXProperty("obstacle", "true")) {
//TMXTiledMapExample.this.mCactusCount++;
//coffins[coffinPtr++] = pTMXTile.getTileRow() * 15 + pTMXTile.getTileColumn();
}
}
});
How do I handle collisions with obstacles so as to prevent the player from walking through the obstacle (i.e., like a wall)?
I believe what you're asking is how do you implement collision handling. To be clear: Collision detection is the step where you determine that something is colliding(overlapping) with something else. Collision handling is where you, say, move one of those things such that it is no longer overlapping. In this case, I'm assuming we're past the collision detection and on to collision handling because you're in a method called "onTMXTileWithPropertiesCreated," which I'm guessing means the player is on such a tile. So here's the idea, put very simply:
When, due to the movement of the player (or some other sprite) you detect that the sprite is colliding with a sprite that you would like to be impassable -- "real" in your terms, you're going to want to move the sprite back the distance that would prevent it from overlapping.
Doing this with rectangles is very simple. Doing it with other shapes gets a little more complicated. Because you're working with a TMX tile map, rectangles will probably work for now. Here's a basic example with rectangles.
public boolean adjustForObstacle(Rect obstacle) {
if (!obstacle.intersect(this.getCollisionRect())) return false;
// There's an intersection. We need to adjust now.
// Due to the way intersect() works, obstacle now represents the
// intersection rectangle.
if (obstacle.width() < obstacle.height()) {
// The intersection is smaller left/right so we'll push accordingly.
if (this.getCollisionRect().left < obstacle.left) {
// push left until clear.
this.setX(this.getX() - obstacle.width());
} else {
// push right until clear.
this.setX(this.getX() + obstacle.width());
}
} else {
if (this.getCollisionRect().top < obstacle.top) {
// push up until clear.
this.setY(this.getY() - obstacle.height());
} else {
// push down until clear.
this.setY(this.getY() + obstacle.height());
}
}
return true;
}
What this is doing is calculating the overlapping rectangle and moving the sprite along the smallest dimension of overlap by the amount that will make it no longer overlap. Since you're using AndEngine, you can make use of the collidesWith() method in IShape, which detects collisions more elegantly than the above approach.
since I use this
if(pTMXTileProperties.containsTMXProperty("obstacle", "true")) {
//TMXTiledMapExample.this.mCactusCount++;
//coffins[coffinPtr++] = pTMXTile.getTileRow() * 15 + pTMXTile.getTileColumn();
//initRacetrackBorders2();
// This is our "wall" layer. Create the boxes from it
final Rectangle rect = new Rectangle(pTMXTile.getTileX()+10, pTMXTile.getTileY(),14, 14);
final FixtureDef boxFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 1f);
PhysicsFactory.createBoxBody(mPhysicsWorld, rect, BodyType.StaticBody, boxFixtureDef);
rect.setVisible(false);
mScene.attachChild(rect);
}
Have fun !
I tried to create a simple collage designer for Android. Each image can be moved, rotated, scaled. Use this code:
var os:Sprite = new Sprite();
os.cacheAsBitmap = true;
os.cacheAsBitmapMatrix = new Matrix();
Multitouch.inputMode = MultitouchInputMode.GESTURE;
if (Multitouch.supportsGestureEvents){
os.addEventListener(TransformGestureEvent.GESTURE_ROTATE , onRotate );
os.addEventListener(TransformGestureEvent.GESTURE_ZOOM , onZoom);
os.addEventListener(TransformGestureEvent.GESTURE_PAN , onPan);
}
os.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
os.addEventListener(MouseEvent.MOUSE_UP, onUp);
protected function onRotate(event:TransformGestureEvent):void
{
event.target.rotation += event.rotation;
}
protected function onZoom(event:TransformGestureEvent):void
{
event.target.scaleX *= event.scaleX;
event.target.scaleY *= event.scaleY;
}
protected function onPan(event:TransformGestureEvent):void
{
event.target.x = event.offsetX;
event.target.y = event.offsetY;
}
protected function onDown(e:MouseEvent):void
{
os.startDrag();
e.stopPropagation();
}
protected function onUp(e:MouseEvent):void
{
os.stopDrag();
}
However, scaling images is not smooth, the image suddenly changes size, motion pull. Although I have quite a powerful device for testing. I can not use a standard way using markers, because the images are quite small, and tap your finger into the marker will be difficult.
Prompt code examples how this can be implemented, please.
Are you using "gpu" renderMode to test?
And try use bitmap instead
So I have a bitmap that I have loaded from a resource file (an PNG image):
Bitmap map = BitmapFactory.decodeResource(getResources(), R.drawable.wave);
If I draw this bitmap only once using canvas.drawBitmap(...); then there is no problem. However, If I draw that very same bitmap multiple times, then the picture keeps flashing back and forth, not steady like before.
I suspected that I cannot use the same bitmap more than once so I tried to load the image into a new bitmap every time when I want to draw the same picture, but it does not help, the behavior still persists.
The program is complicated, but basically, I want to draw a ocean wave. I have a image of a small wave. To make the effect of the wave moving from the left edge of the screen to the right edge. I keep track of the position of the left edge of the bitmap.
// The ocean.
private ArrayList<Wave> waves;
// Draw the waves and update their positions.
for (int i = 0; i < this.waves.size(); i++)
{
Wave wave = this.waves.get(i);
// Go through each of the sub-waves of this current wave.
for (int j = 0; j < wave.getSubWaveEdges().size(); j++)
{
// Get the sub wave.
final float subWaveEdge = wave.getSubWaveEdges().get(j);
canvas.drawBitmap( wave.getSubWave(j), subWaveEdge, 40, brush);
wave.setSubWaveEdge(j, subWaveEdge + (float) 0.5);
}
// Update this current wave.
wave.update();
// If the wave has passed the left edge of the screen then add a new sub-wave.
if (wave.getFarthestEdge() >= 0)
wave.addSubWaveEdges(wave.getFarthestEdge() - this.getWidth());
}
If the left edge of a bitmap is inside the screen then I create a new bitmap from the same image file and draw. Here is the class Wave:
private class Wave
{
private Bitmap wave;
private float farthestEdge;
private ArrayList<Float> subWaveEdges;
private ArrayList<Bitmap> subWaves;
public Wave(Bitmap wave)
{
this.wave = wave;
this.farthestEdge = 0;
this.subWaveEdges = new ArrayList<Float>();
this.subWaves = new ArrayList<Bitmap>();
}
public Bitmap getWave ()
{ return this.wave; }
public void setWave (Bitmap wave)
{ this.wave = wave; }
public float getFarthestEdge ()
{ return this.farthestEdge; }
public void setFarthestEdge (final float furthestEdge)
{ this.farthestEdge = furthestEdge; }
public ArrayList<Float> getSubWaveEdges ()
{ return subWaveEdges; }
public void setSubWaveEdge (final int index, final float value)
{
this.subWaveEdges.remove(index);
this.subWaveEdges.add(value);
}
public void addSubWaveEdges (final float edge)
{
this.subWaveEdges.add(edge);
Bitmap newSubWave = BitmapFactory.decodeResource(getResources(), R.drawable.wave);
newSubWave = Bitmap.createScaledBitmap(newSubWave, MasterView.this.getWidth(), newSubWave.getHeight(), true);
this.subWaves.add(newSubWave);
}
public Bitmap getSubWave(final int index)
{ return this.subWaves.get(index); }
public void update ()
{
// Check to see if there is any sub-wave going outside of the screen.
// If there is then remove that wave.
for (int index = 0; index < this.subWaveEdges.size(); index++)
if (this.subWaveEdges.get(index) > MasterView.this.getWidth())
{
this.subWaveEdges.remove(index);
this.subWaves.remove(index);
}
// Set the farthest edge to the other side of the screen.
this.farthestEdge = MasterView.this.getWidth();
// Get the farthest edge of the wave.
for (int index = 0; index < this.subWaveEdges.size(); index++)
if (this.subWaveEdges.get(index) < this.farthestEdge)
this.farthestEdge = this.subWaveEdges.get(index);
}
}
Another suspicion that I have is that may be when I create two bitmaps from the same resource file, the pixels of the image are divided among two bitmaps, meaning that each bitmap only gets part of the pixels, not all. I am suspecting this because when the bitmaps are drawn, the parts where they overlaps are drawn steadily, no flashing.
Anyone has stumbled upon this problem and know how to fix?
Thanks,
Viktor Lannér, Thank you for helping, but I don't think that's the problem. I understand it is hard to read my codes since it is only a small piece of the big program.
However, I found the problem: This is not mentioned in my original question, but in order to simulate the two waves moving after one another, I have to draw the next wave as soon as the first wave enters the screen. However, each wave is longer than the width of the screen. Therefore, I have to draw the next wave from "outside" the screen if you know what I mean. It means that the next wave is drawn from a negative x-coordinate from outside the screen:
// If the wave has passed the left edge of the screen then add a new sub-wave.
if (wave.getFarthestEdge() >= 0)
wave.addSubWaveEdges(wave.getFarthestEdge() - this.getWidth());
And I found out that it does not like this. This is what causes the flashing back and forth.
In order to fix this, instead of drawing the next wave from outside the screen, I use this method:
canvas.drawBitmap (Bitmap bitmap, Rect source, Rect destination, Paint paint)
This method allows you to specify a rectangular region on the bitmap to be drawn to the screen and a rectangular region on the screen where that part of the bitmap will be drawn over. I use this method to draw the next wave. As the next wave moves into the screen, I change the "source" and "destination" appropriately to draw parts of the bitmap.
I just wanted to say that I had an issue where the images on my canvas were flashing back and forth, or, flashing between black and my first frame until I made a movement, almost as if the canvas was rapidly switching between its current and last image.
This might have had something to do with your situation, and to fix it I found out that it was because I was locking the canvas every frame, even when I had nothing to draw. For whatever reason, that lock, I think, created this situation.
I got around it by doing something like this:
if (needToRedraw == true) {
canvas = mSurfaceHolder.lockCanvas(null);
... logic to eventually draw on that canvas ...
}
Before canvas.drawBitmap(...) call; try to use canvas.drawColor(Color.BLACK) to clear the Canvas from previous drawings.
Sample code:
// Stuff.
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(wave.getSubWave(j), subWaveEdge, 40, brush);
// Stuff.