Create a random, sine graph like landscape sprite

前端 未结 1 1038
囚心锁ツ
囚心锁ツ 2021-01-18 17:54

Lets say I have this sprite:

\"Sample

And I created a random landscape during runtime:

相关标签:
1条回答
  • 2021-01-18 18:13

    I don't really know about Box2D or AndEngine, but I thought this was an interesting problem and made a custom SurfaceView that can draw a random "terrain" like the one in your picture. (Hopefully it will be of use to you or someone else, at least I learnt some new stuff :p)

    Single colour terrain: screenshot Tiled background-terrain: screenshot The tiled bitmap:
    resource bitmap

    My code is as follows:

    public class PathView extends SurfaceView implements SurfaceHolder.Callback{
    private class DrawingRunnable implements Runnable{
        private final static int minPointsOnScreen = 3;
        SurfaceHolder surfaceHolder;
        Random rand = new Random();
    
        private Path path;
        private Paint pathPaint;
        Bitmap background;
        private Paint tilePaint;        
    
        volatile boolean running = false;
    
        int width;
        int height;
        int maxHeight;
    
        protected DrawingRunnable(SurfaceHolder sh){
            surfaceHolder = sh;
    
            pathPaint = new Paint();
            pathPaint.setColor(0xFF000000);
            pathPaint.setStrokeWidth(4);
    
            tilePaint = new Paint();
        }
    
        protected void createPath(){
            path = new Path();
            path.setFillType(Path.FillType.WINDING);
    
            path.setLastPoint(0, height);
            int lastX = 0, lastY = height - rand.nextInt(maxHeight);
            path.lineTo(lastX,lastY);
    
            int newX=lastX, newY=lastY;
    
            do{ 
                lastX = newX; lastY = newY;
                newX += rand.nextInt(width/minPointsOnScreen);
                newY = height - rand.nextInt(maxHeight);
                path.cubicTo(
                        interpolateLinear(lastX, newX, 0.333f),
                        lastY,
                        interpolateLinear(lastX, newX, 0.666f),
                        newY,
                        newX, newY);
            }while(newX <= width);
    
            path.lineTo(width, height);
        }
    
        private int interpolateLinear(int start, int end, float part){
            return (int) (start*(1-part) + end*part);
        }
    
        @Override
        public void run(){
            while(running){
                Canvas c = null;
                try{
                    c = surfaceHolder.lockCanvas(null);
                    synchronized (surfaceHolder) { 
                        doDraw(c); 
                    }
                } finally{ if(c!=null) surfaceHolder.unlockCanvasAndPost(c); }
                SystemClock.sleep(40);
            }
        }
    
        private void doDraw(Canvas c){
            c.drawColor(0xFFFFFFFF);
            //c.drawPath(path, pathPaint); //Use this to draw a single-colour. (First screenshot)
            c.clipPath(path);
            for(int y = 0; y+background.getHeight() < height+background.getHeight(); y+=background.getHeight()){
                for(int x = 0; x+background.getWidth() < width+background.getWidth(); x+=background.getWidth()){
                    c.drawBitmap(background, x, y, tilePaint);
                }
            }
        }
    }
    
    private ExecutorService exec;
    private SurfaceHolder holder;
    private DrawingRunnable drawer;
    
    public PathView(Context c){ super(c); init(c); }
    public PathView(Context c, AttributeSet as){ super(c, as); init(c); }
    public PathView(Context c, AttributeSet as, int defStyle){ super(c, as, defStyle); init(c); }
    
    private void init(Context c){
        exec = Executors.newSingleThreadExecutor();
        holder = getHolder();
        holder.addCallback(this);
    }
    
    public void surfaceCreated(SurfaceHolder sh){
        if( drawer == null ){
            drawer = new DrawingRunnable(holder);
            drawer.width = getWidth();
            drawer.height = getHeight();
            drawer.maxHeight = drawer.height/2;
            drawer.createPath();
            drawer.background = BitmapFactory.decodeResource(getResources(), R.drawable.tile);
        }
        drawer.running = true;
        exec.execute(drawer);
    }
    public void surfaceDestroyed(SurfaceHolder sh){
        drawer.running = false;
    }
    public void surfaceChanged(SurfaceHolder sh, int format, int width, int height){}
    }
    

    If this is of any help to you you'll probably have to play around with the parameters to get shapes that suits your needs and most likely add a parameter for minimum distance between points etc. It is also a good idea to optimize the drawing of the background a bit, like drawing from the bottom up to the maximum height of the terrain, to minimize drawing to invisible areas. I should also be possible to reduce the amount of calls to getHeight() and getWidth().

    Cheers!

    0 讨论(0)
提交回复
热议问题