まず、私のラインの一部を自由に削除する必要があるため、これは他の「スムーズライン」の質問と重複しません。私の行を保存する特別な方法。特別なラインをスムーズにフォローする必要があります。他の動作もあります。
私はユーザーの指に沿って線を引く必要があります。しかし、私はまた、この行の末尾を自由に削除することができる必要があります。
基本的に私はこのゲームでは、ユーザーのマウスを以下の青色の線のように見えるように、このラインの動作を必要とする:私は追加私のonTouch方法でいくつかのコードを持ってこれを行うには
http://hakim.se/experiments/html5/coil/
ユーザーの指が動くたびに配列をポイントします。
@Override
public boolean onTouch(View v, MotionEvent event) {
//This for loop is supposed to add all points that were in between this
//motion event and the previous motion event to the "linePoints" array.
for(int i = 0; i < event.getHistorySize(); i++) {
linePoints[arrayIndex] = new Point((int) event.getHistoricalX(i), (int) event.getHistoricalY(i));
arrayIndex++;
}
//This adds the current location of the user's finger to "linePoints"
// array
linePoints[arrayIndex] = new Point((int) event.getX(), (int) event.getY());
arrayIndex++;
//This switch statement makes it so that if the user lifts their finger
// off the screen the line will get deleted.
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
screenPressed = true;
setEventTime(); //Ignore setEventTime();
break;
case MotionEvent.ACTION_UP:
screenPressed = false;
linePoints = new Point[10000];
arrayIndex = 0;
break;
}
return true;
}
はその後onDraw()メソッドではゲームはライン上のすべての点を描画します。
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
//This code loops through all of linePoints and then draws every point
// in linePoints to create a line on screen.
for(Point p : linePoints) {
if(p == null) {
break;
}
canvas.drawRect(p.x, p.y, p.x+ 2, p.y + 2, black);
invalidate();
// I have not added in the deletion behavior yet, because my current
// method cannot create a smooth line.
}
私が代わりにAndroidの パス()クラスを使用してのラインを作るためのポイントを描画することを選んだ理由なぜなら私は自由に行の一部を削除したいからです(配列 "linePoints"からポイントを削除することによって)。
問題は、指をあまりに速く動かしてポイントが広がり、ラインのように見えなくなることです。
どのようにしてラインがスムーズになりますが、その一部を削除できるように保存することもできますか?
編集:私が提供するように、どのように線が詳細にされるかについての詳細が誰かに尋ねられました。
「X」秒以上行を描画している場合は、その行を削除したいと考えています。私が行を削除する方法は次のとおりです。
行の終わりは、行が完全に削除されるまで、またはユーザーが画面から指を離すまで、(ユーザーがそれを描いているまで)消えていきます。
EDIT 2:ラインが交差しているかどうかを知る必要があります。そのため、ポイントストレージシステムを選択した理由は、アレイの2ポイントが同じ座標を持っていたら私はラインが交差したかどうかを知るだろう)。私は現在、これをどのように実装するのか考えていません(ポイントが連続していないためです)が、何かを見つけたらさらに編集を行います。
EDIT 3:ポイントが散発的に離れていても、ラインが交差するかどうかを判断する解決策を見つけました。しかし、私はまだギャップのない滑らかな線を作るという問題を解決していません。
ソリューション:
たびにゲームは、それが配列とモデル線分「A」に追加し、前のポイントにそれを比較します配列に新しいポイントを追加します。次に、線分 "A"と、配列内の2点から作成されたすべての前の線分を比較し、比較された線分が交差するかどうかを判断します。彼らがそうするなら、私はラインに交差点があることを知っています。
EDIT 4:これは、現在使用している最新の完全なコードです。詳細なコメントを提供するために、このコードI(TRY)と私の目標と私がこれまで行っているの説明上部の概要内部
。
この大きなコードの前に、私の現在の問題は、ユーザーが特定の時間以上線を描いていれば、一定のペース(例えば10ミリメートル/秒)で線を削除できることです。
package com.vroy.trapper;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class GameView extends View implements View.OnTouchListener {
// I am basically trying to replicate the game: http://hakim.se/experiments/html5/coil/
// without any of the lighting effects and my version will have slightly
// different behavior.
// Right now all I am concerned with is allowing the line to be deleted at a constant pace
// if the user has been drawing a line for more than "X" seconds.
/*
OVERVIEW:
array of points "linePoints" stores all locations of user touching screen
that are captured by system.
Each time a new point is added to "linePoints" I draw a path from the previous point
to the new point. (Sidenote: While this does make the line look very smooth it can still look odd sometimes)
The game also checks for intersections in the line to see if the line has made a
polygon. I do this because this is important for a feature that will be implemented.
The system then draws the path on screen.
The system also checks if the user has lifted their finger off the screen,
if the user has then the system deletes the current line on screen and resets all variables.
TO BE IMPLEMENTED:
If the line has formed a polygon then the game will check if that polygon contains certain
objects that will randomly spawn onscreen.
PROBLEMS:
1. Currently I want the line to start deleting itself from the back if the user
has been drawing the line for more then "X" seconds. However I am not sure how to do this.
*/
// General variables.
private int screenWidth;
private int screenHeight;
public static boolean screenPressed; //Might not need.
// public static float contactLocX;
// public static float contactLocY;
//Time variables.
private static long startTime; //This variable is used in conjunction with the
//elapsedTime() method to determine if the user
// has been drawing a line for more then "X" seconds.
//Game variables.
private static int orbWidth; //Not used currently. This will be the width of the randomly spawned tokens.
private Point[] linePoints; //The array that holds all captured points.
private int arrayIndex;
private Path linePath; //The path that the canvas draws.
private boolean firstPoint; //If firstPoint is true then that means is 1st point in current line.
//I need this for the path.MoveTo() method.
//Debug values. (Not used currently)
private int debug;
private String strdebug;
//Paints
Paint black = new Paint();
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
black.setARGB(255, 0, 0, 0); //Paint used to draw line.
black.setStyle(Paint.Style.STROKE);
black.setStrokeWidth(3);
linePoints = new Point[10000];
GameView gameView = (GameView) findViewById(R.id.GameScreen); //Setting up onTouch listener.
gameView.setOnTouchListener(this);
arrayIndex = 0;
linePath = new Path(); //Setting up initial path.
firstPoint = true;
}
//Currently OnSizeChanged is not needed, I only keep it for the future when I implement
// the random object spawning system.
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
screenHeight = getHeight();
screenWidth = getWidth();
orbWidth = screenHeight/20;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(linePath, black);
//Currently "1000000000" is a placeholder value (in nano-seconds)
if(elapsedTime() > 1000000000) {
//Code that evenly deletes the line starting from the back
//(this is where I most need your assistance).
}
invalidate(); //I don't know if this is the best way to refresh the screen
}
@Override
public boolean onTouch(View v, MotionEvent event) {
//Sets up starting point of path
if(firstPoint) {
firstPoint = false;
linePath.moveTo(event.getX(),event.getY());
linePoints.add(new TimeStampedPoint((int)event.getX(), (int)event.getY(),event.getEventTime()));
}
//Adds points to path & linePoints that were missed.
for(int i = 0; i < event.getHistorySize(); i++) {
linePoints[arrayIndex] = new Point((int) event.getHistoricalX(i), (int) event.getHistoricalY(i));
linePath.lineTo(linePoints[arrayIndex].x,linePoints[arrayIndex].y);
if(arrayIndex >= 1) {
checkForIntersections(linePoints[arrayIndex - 1], linePoints[arrayIndex]);
}
arrayIndex++;
}
//Adds current point to path & linePath();
linePoints[arrayIndex] = new Point((int) event.getX(), (int) event.getY());
if (arrayIndex >= 1) {
checkForIntersections(linePoints[arrayIndex - 1] ,linePoints[arrayIndex]);
}
linePath.lineTo(linePoints[arrayIndex].x,linePoints[arrayIndex].y);
arrayIndex++;
//This switch statements creates initial actions for when the finger is pressed/lifted.
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
screenPressed = true;
setEventTime(); //This starts the timer that will eventually reach "X" seconds.
break;
case MotionEvent.ACTION_UP: //The primary purpose of this "switch" is to delete the old line
// & reset variables in preparation for new line
screenPressed = false;
linePoints = new Point[10000]; //Possibly filling heap with empty arrays.
linePath = new Path();
arrayIndex = 0;
firstPoint = true;
break;
}
return true;
}
private void checkForIntersections(Point p, Point p2) {
for(int i = arrayIndex - 3; i > 0; i--) {
if(intersect(p,p2,linePoints[i],linePoints[i-1])) {
//RETURN POINTS IN THE POLYGON THAT WILL BE USED TO DETERMINE IF "TOKENS"
// ARE IN POLYGON.
}
}
}
private void setEventTime() {
startTime = System.nanoTime();
}
//Checks current time since setEventTime
private long elapsedTime() {
return System.nanoTime() - startTime;
}
// Things used to determine intersections.
//Used to determine orientation of <something>
private static int orientation(Point p, Point q, Point r) {
double val = (q.y - p.y) * (r.x - q.x)
- (q.x - p.x) * (r.y - q.y);
if (val == 0.0)
return 0; // colinear
return (val > 0) ? 1 : 2; // clock or counterclock wise
}
//Determines intersection of 2 lines (P1,Q1) & (P2,Q2).
private static boolean intersect(Point p1, Point q1, Point p2, Point q2) {
int o1 = orientation(p1, q1, p2);
int o2 = orientation(p1, q1, q2);
int o3 = orientation(p2, q2, p1);
int o4 = orientation(p2, q2, q1);
if (o1 != o2 && o3 != o4)
return true;
return false;
}
//Will shorten checking process by determining if 2 lines do/don't have the same bounding box.
//Not yet implemented.
private static boolean boundBoxCheck(Point p1, Point q1, Point p2, Point q2) {
return true; //Placeholder code
}
}
EDIT 5:
私が原因境界エラーのうちインデックスにstKentさん/タイタンのコードと私のコードのクラッシュの私の実装を行いました。
私は、問題を見つけて、それを修正しようとしますが、私は他の誰かがそれを固定で手を取りたい包み、私はここに私のコードを掲載しますやるまで。
package com.vroy.trapper;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.sql.Time;
import java.util.ArrayList;
import java.util.List;
public class GameView extends View implements View.OnTouchListener {
// I am basically trying to replicate the game: http://hakim.se/experiments/html5/coil/
// without any of the lighting effects and my version will have slightly
// different behavior.
// Right now all I am concerned with is allowing the line to be deleted at a constant pace
// if the user has been drawing a line for more than "X" seconds.
/*
OVERVIEW:
array of points "linePoints" stores all locations of user touching screen
that are captured by system.
Each time a new point is added to "linePoints" I draw a path from the previous point
to the new point. (Sidenote: While this does make the line look very smooth it can still look odd sometimes)
The game also checks for intersections in the line to see if the line has made a
polygon. I do this because this is important for a feature that will be implemented.
The system then draws the path on screen.
The system also checks if the user has lifted their finger off the screen,
if the user has then the system deletes the current line on screen and resets all variables.
TO BE IMPLEMENTED:
If the line has formed a polygon then the game will check if that polygon contains certain
objects that will randomly spawn onscreen.
PROBLEMS:
1. Currently I want the line to start deleting itself from the back if the user
has been drawing the line for more then "X" seconds. However I am not sure how to do this.
*/
// General variables.
private int screenWidth;
private int screenHeight;
public static boolean screenPressed; //Might not need.
// public static float contactLocX;
// public static float contactLocY;
//Time variables.
private static long startTime; //This variable is used in conjunction with the
//elapsedTime() method to determine if the user
// has been drawing a line for more then "X" seconds.
//Game variables.
private static int orbWidth; //Not used currently. This will be the width of the randomly spawned tokens.
private List<TimeStampedPoint> linePoints; //The array that holds all captured points.
private int arrayIndex;
private Path linePath; //The path that the canvas draws.
private List<TimeStampedPoint> validPoints;
private boolean firstPoint; //If firstPoint is true then that means is 1st point in current line.
//I need this for the path.MoveTo() method.
//Debug values. (Not used currently)
private int debugint;
private String strdebug;
//Paints
Paint black = new Paint();
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
black.setARGB(255, 0, 0, 0); //Paint used to draw line.
black.setStyle(Paint.Style.STROKE);
black.setStrokeWidth(3);
linePoints = new ArrayList<>();
validPoints = new ArrayList<>();
GameView gameView = (GameView) findViewById(R.id.GameScreen); //Setting up onTouch listener.
gameView.setOnTouchListener(this);
arrayIndex = 0;
linePath = new Path(); //Setting up initial path.
validPoints = new ArrayList<>();
firstPoint = true;
}
//Currently OnSizeChanged is not needed, I only keep it for the future when I implement
// the random object spawning system.
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
screenHeight = getHeight();
screenWidth = getWidth();
orbWidth = screenHeight/20;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
linePath.rewind();
validPoints = removeExpiredPoints();
updatePathUsingPoints(validPoints);
canvas.drawPath(linePath, black);
linePoints = validPoints;
invalidate(); //I don't know if this is the best way to refresh the screen
}
@Override
public boolean onTouch(View v, MotionEvent event) {
debugint = arrayIndex;
strdebug = Integer.toString(debugint);
Log.i("ARRAY INDEX: ",strdebug);
debugint = linePoints.size();
strdebug = Integer.toString(debugint);
Log.i("LIST SIZE: ",strdebug);
//Sets up starting point of path
if(firstPoint) {
firstPoint = false;
linePath.moveTo(event.getX(),event.getY());
linePoints.add(new TimeStampedPoint((int)event.getX(),(int)event.getY(),event.getEventTime()));
}
//Adds points to path & linePoints that were missed.
for(int i = 0; i < event.getHistorySize(); i++) {
linePoints.add(new TimeStampedPoint((int) event.getHistoricalX(i), (int) event.getHistoricalY(i), event.getHistoricalEventTime(i)));
linePath.lineTo(linePoints.get(arrayIndex).x,linePoints.get(arrayIndex).y);
if(arrayIndex >= 1) {
checkForIntersections(linePoints.get(arrayIndex), linePoints.get(arrayIndex));
}
arrayIndex++;
}
//Adds current point to path & linePath();
debugint = linePoints.size();
strdebug = Integer.toString(debugint);
Log.i("Before" , strdebug);
linePoints.add(new TimeStampedPoint((int) event.getX(), (int) event.getY(),event.getEventTime()));
debugint = linePoints.size();
strdebug = Integer.toString(debugint);
Log.i("After:", strdebug);
if (arrayIndex >= 1) {
checkForIntersections(linePoints.get(arrayIndex - 1) ,linePoints.get(arrayIndex));
}
linePath.lineTo(linePoints.get(arrayIndex).x,linePoints.get(arrayIndex).y);
arrayIndex++;
//This switch statements creates initial actions for when the finger is pressed/lifted.
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
screenPressed = true;
setEventTime(); //This starts the timer that will eventually reach "X" seconds.
break;
case MotionEvent.ACTION_UP: //The primary purpose of this "switch" is to delete the old line
// & reset variables in preparation for new line
screenPressed = false;
linePoints.clear();
linePath = new Path();
arrayIndex = 0;
firstPoint = true;
break;
}
return true;
}
private void checkForIntersections(TimeStampedPoint p, TimeStampedPoint p2) {
for(int i = arrayIndex - 3; i > 0; i--) {
if(intersect(p,p2,linePoints.get(i),linePoints.get(i-1))) {
//RETURN POINTS IN THE POLYGON THAT WILL BE USED TO DETERMINE IF "TOKENS"
// ARE IN POLYGON.
}
}
}
private void setEventTime() {
startTime = System.nanoTime();
}
//Checks current time since setEventTime
private long elapsedTime() {
return System.nanoTime() - startTime;
}
// Things used to determine intersections.
//Used to determine orientation of <something>
private static int orientation(Point p, Point q, Point r) {
double val = (q.y - p.y) * (r.x - q.x)
- (q.x - p.x) * (r.y - q.y);
if (val == 0.0)
return 0; // colinear
return (val > 0) ? 1 : 2; // clock or counterclock wise
}
//Determines intersection of 2 lines (P1,Q1) & (P2,Q2).
private static boolean intersect(TimeStampedPoint p1, TimeStampedPoint q1, TimeStampedPoint p2, TimeStampedPoint q2) {
int o1 = orientation(p1, q1, p2);
int o2 = orientation(p1, q1, q2);
int o3 = orientation(p2, q2, p1);
int o4 = orientation(p2, q2, q1);
if (o1 != o2 && o3 != o4)
return true;
return false;
}
//Will shorten checking process by determining if 2 lines do/don't have the same bounding box.
//Not yet implemented.
private static boolean boundBoxCheck(Point p1, Point q1, Point p2, Point q2) {
return true; //Placeholder code
}
//Point class that also stores time of creation
@SuppressLint("ParcelCreator")
private static class TimeStampedPoint extends Point {
private final long timeStamp;
private TimeStampedPoint(final int x, final int y, final long timeStamp) {
super(x, y);
this.timeStamp = timeStamp;
}
}
private List<TimeStampedPoint> removeExpiredPoints() {
final List<TimeStampedPoint> result = new ArrayList<>();
for (final TimeStampedPoint point: linePoints) {
if (System.currentTimeMillis() - point.timeStamp <= 10000) {
// We only include points in the result if they are not expired.
result.add(point);
}
}
return result;
}
private void updatePathUsingPoints(final List<TimeStampedPoint> validPoints) {
if (validPoints.size() < 2) {
return; // Return the empty path here; nothing to draw.
}
linePath.moveTo(validPoints.get(0).x,validPoints.get(0).y);
for (int i = 1; i < validPoints.size(); i++) {
final Point targetPoint = validPoints.get(i);
linePath.lineTo(targetPoint.x, targetPoint.y);
}
}
}
私は注意する必要がある非常に非常に重要なこともあります。 私はそれが編集4までこれを注目していない私のせいであると考えているが、私はラインで私もそれが均等に削除することを希望エンドから削除したい一方で、私はstkentが提供する現在のコードを考え、タイタンがでポイントを削除します(ポイントが不均一に広がっているので)、一貫したペースでラインしかし、実際にライン自体を意味するものではありません一貫したペースで削除されます。多数の編集を通して私にこだわっためのみんなに
多くのおかげで私は解決策は、ラインが一貫したペースで削除することを可能にすることを見つけることができます願っています今まで。
パスを描くことで、あらゆる点の間。しかし、私はまだ一定のペースでラインを削除するための解決策はありません。違うパスは長さが違っていて削除のペースが不均一になるからです。 – Roymunson
描画されたパスに沿って補間して、新しいエンドポイントを見つけることはできますか?それは、少なくとも部分的な削除の錯覚を許すでしょう。アーク長でパスを簡単にパラメータ化できるかどうかは覚えていない。 – stkent
@stkent私は実際に描画されたパスに沿って補間するために熱い知らない。おそらくあなたは答えとしてコードを投稿することができますか? – Roymunson