我想开发一个小游戏,其中橙色矩形在显示屏上从右到左动画,当它位于中间(绿色矩形)时,用户应按下按钮并在他/她成功时获得积分(参见截屏):
这里是游戏类的代码
public class Test extends Fragment implements View.OnClickListener {
/*
Game variables
*/
public static final int DELAY_IN_MILLIS = 100;
ArrayList<View_Game_Event_Rectangle> arrayList_GameEventRectangles;
private int currentTimeSlot;
private boolean gameIsRunning;
private Handler handler = new Handler();
private int timeslotsEventElementUntilTheMiddleOfTheScreen_Level1 = (int)(5.0 * (1000.0/DELAY_IN_MILLIS));
private int numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 = 5000;
//Fix parameters of the UI elements
private float horizontalPercentageOfScoreRectangle_End = 0.491f;
private float horizontalPercentageOfScoreRectangle_Start = 0.567f;
private float verticalBiasOfEventElementToBeInTheLine = 0.049f;
private float percentageHeightOfEventElement = 0.071f;
int widthDisplay;
int heightDisplay;
int helpCounterRun =0;
int helpCounterUpdateScreen =0;
int helpCounterCountDownTime =0;
private FragmentTestBinding binding;
private ConstraintLayout constraintLayout;
ConstraintSet constraintSet ;
//Variables for the single view event
View_Game_Event_Rectangle[] viewEvent;
private boolean fragmentViewHasBeenCreated = false;
private CountDownTimer cdt;
private final long DURATION = 900000L;
private final long DELAY_COUNT_DOWN_TIMER = 100;
private int numberOfTimeSlotsUntilTheEndOfScreen = (int)(numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 * 2/(DELAY_COUNT_DOWN_TIMER));
private int currentLevel;
public Test() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
arrayList_GameEventRectangles = new ArrayList<View_Game_Event_Rectangle>();
currentLevel = 1;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentTestBinding.inflate(inflater, container, false);
WindowManager wm = (WindowManager) getActivity().getWindowManager();
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
widthDisplay = size.x;
heightDisplay = size.y;
container.getContext();
constraintLayout= binding.constraintLayout;
fragmentViewHasBeenCreated = true;
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
constraintLayout = binding.constraintLayout;
constraintSet = new ConstraintSet();
startGame();
return binding.getRoot();
}
@Override
public void onClick(View view) {
}
public void startGame () {
gameIsRunning = true;
Log.e("LogTag_El", "-------Start Game------" );
//Create rectangle-element that should be animated from the right to the left on the screen
arrayList_GameEventRectangles.add(new View_Game_Event_Rectangle(getActivity(), "Bulb Rectangle", 100, 20));
countDownTime();
}
private void updateScreen() {
for (int currentElement =0; currentElement <arrayList_GameEventRectangles.size(); currentElement++) {
//Create view and set
if (currentTimeSlot == arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot() - 50) {
arrayList_GameEventRectangles.get(currentElement).setActive(true);
//Set the parameters and the backgrund of the view element
arrayList_GameEventRectangles.get(currentElement).setLayoutParams(new ViewGroup.LayoutParams(0, 0));
if(arrayList_GameEventRectangles.get(currentElement).getEventType().equals("Bulb Rectangle")) {
arrayList_GameEventRectangles.get(currentElement).setBackground(ContextCompat.getDrawable(getActivity(),R.drawable.game_event_rectangle_bulb_1).mutate());
}
arrayList_GameEventRectangles.get(currentElement).setId(View.generateViewId());
//Make the view invisible (before it's appearence time)
arrayList_GameEventRectangles.get(currentElement).getBackground().setAlpha(0);
// Set the ConstraintLayout programatically for the view
constraintLayout.addView(arrayList_GameEventRectangles.get(currentElement));
constraintSet.clone(constraintLayout);
constraintSet.constrainPercentHeight(arrayList_GameEventRectangles.get(currentElement).getId(), percentageHeightOfEventElement);
float widthConstrainPercentage_element1 = (float)(arrayList_GameEventRectangles.get(currentElement).getDuration() / 100.0);
float duration = arrayList_GameEventRectangles.get(currentElement).getDuration();
constraintSet.constrainPercentWidth(arrayList_GameEventRectangles.get(currentElement).getId(), widthConstrainPercentage_element1);
constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID,ConstraintSet.BOTTOM,0);
constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.TOP,ConstraintSet.PARENT_ID ,ConstraintSet.TOP,0);
constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.LEFT,ConstraintSet.PARENT_ID ,ConstraintSet.LEFT,0);
constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.RIGHT,ConstraintSet.PARENT_ID ,ConstraintSet.RIGHT,0);
float horizontalBias = 1.0f ;
constraintSet.setHorizontalBias(arrayList_GameEventRectangles.get(currentElement).getId(), horizontalBias);
constraintSet.setVerticalBias(arrayList_GameEventRectangles.get(currentElement).getId(), verticalBiasOfEventElementToBeInTheLine);
constraintSet.applyTo(constraintLayout);
}
//Shift the view to the right border of the display
if (currentTimeSlot == arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot() - 40) {
arrayList_GameEventRectangles.get(currentElement).setTranslationX(arrayList_GameEventRectangles.get(currentElement).getWidth());
}
//Animate view element
if (currentTimeSlot ==arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot()) {
arrayList_GameEventRectangles.get(currentElement).getBackground().setAlpha(255);
long durationForTheAnimation = (long)(numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 * 1);
arrayList_GameEventRectangles.get(currentElement).animate().setDuration(durationForTheAnimation).translationX(widthDisplay*(-1)).start();
}
helpCounterUpdateScreen++;
checkPositionsOfActiveElements();
}
}
private void countDownTime(){
cdt = new CountDownTimer(DURATION, DELAY_COUNT_DOWN_TIMER) {
boolean delay = true;
public void onTick(long millisUntilFinished) {
if(delay) {
delay = false;
} else {
helpCounterCountDownTime++;
currentTimeSlot++;
updateScreen();
delay = true;
}
}
public void onFinish() {
updateScreen();
}
}.start();
}
public void checkPositionsOfActiveElements () {
for (int currentElement =0; currentElement <arrayList_GameEventRectangles.size(); currentElement++) {
//Check if the view is still running
if (arrayList_GameEventRectangles.get(currentElement).isActive() == true && currentElement==0) {
Log.e("LogTag_FGa_CP", "currentTimeSlot: " + currentTimeSlot);
Log.e("LogTag_FGa_CP", "currentElement.getX(): " + arrayList_GameEventRectangles.get(currentElement).getX());
}
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
// Reset your variable to false
fragmentViewHasBeenCreated = false;
// And clean up any postDelayed callbacks that are waiting to fire
cdt.cancel();
handler.removeCallbacksAndMessages(null);
}
}
这是 XML 布局文件:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/imageView_TargetRectangle"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/rectangle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.16"
app:layout_constraintHorizontal_bias="0.535"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.01"
app:layout_constraintWidth_percent="0.10" />
<Button
android:id="@+id/button_action"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Action"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.102"
app:layout_constraintHorizontal_bias="0.373"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.745"
app:layout_constraintWidth_percent="0.13" />
</androidx.constraintlayout.widget.ConstraintLayout>
为视图设置动画并倒计时我使用处理程序
CountDownTimer
。实际上动画本身是有效的(我首先将不可见的橙色矩形移到最右边,然后在一定时间后使其可见并设置动画)。问题是,我无法准确确定橙色矩形的位置。我只能在一定时间跨度后确定它,这对我想做的事情来说还不够。
这是 LogTag 的输出:
2023-03-14 18:37:40.318 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 100
2023-03-14 18:37:40.320 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 2028.0
2023-03-14 18:37:40.617 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 101
2023-03-14 18:37:40.618 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 2013.0167
2023-03-14 18:37:40.857 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 102
2023-03-14 18:37:40.857 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1972.158
2023-03-14 18:37:41.149 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 103
2023-03-14 18:37:41.150 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1883.3052
2023-03-14 18:37:41.403 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 104
2023-03-14 18:37:41.403 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1780.0686
2023-03-14 18:37:41.668 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 105
2023-03-14 18:37:41.669 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1634.5706
2023-03-14 18:37:41.931 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 106
2023-03-14 18:37:41.936 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1495.0557
2023-03-14 18:37:42.185 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 107
2023-03-14 18:37:42.186 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1317.382
2023-03-14 18:37:42.481 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 108
2023-03-14 18:37:42.481 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1088.4066
2023-03-14 18:37:42.760 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 109
如您所见,当我检查它时,橙色矩形的 x 位置减少了大量像素(例如 1972.158、1883.3052、1780.0686)。我真正想要的是一种方法,它可以返回橙色矩形的每个位置,意思是:1972、1971、1970、1969 等。这样,我可以确保 - 在按下按钮后 - 得到橙色矩形的确切位置。
提醒:您是否需要更多信息,或者为什么没有人至少给我一个可能的解决方案的建议。我会非常感激。
除了使用 x 和 y,您还可以获得屏幕上的确切位置:
Java
var location = new int[2];
view.getLocationOnScreen(location);
科特林
val location = IntArray(2)
view.getLocationOnScreen(location)
我真正想要的是一种方法,可以返回橙色矩形的每个位置,意思是:1972、1971、1970、1969 等。
矩形不一定会出现在这些位置(1972、1971 等),即使动画跨越这些位置。 Android 设备以每秒 60 帧的速率 (Frame Rate)(对于某些设备可能为 90 FPS 或 120 FPS)组成显示内容。对于 60 FPS,每 16 毫秒左右显示一帧。
让我们考虑一个动画,它在一秒钟内将视图的 x 位置从 0 像素移动到 1,000 像素。动画有这三个约束:
为了满足这些条件,假设速度恒定,动画必须将视图平均移动 1,000/16 像素或每帧 62.5 像素。
所以,人们会期望 x 值遵循序列“0、62.5、125、187.5,...”,这会跳过很多位置。这与电影放映机的操作方式没有什么不同。电影的每一帧都代表时间的量化飞跃。
考虑到所有这些,一个解决方案是根据上述内容确定构成成功点击按钮的位置范围。您也可以创建一次移动一个像素的动画,但是由于帧速率限制,动画可能太慢,因为您每秒最多可以移动 60 个像素。
以下是一些可能有帮助的附加信息:
此视图的视觉 x 位置,以像素为单位。这相当于 translationX 属性加上当前的 left 属性。
这是正在动画的
translationX
属性。
这是大纲,尽管您可能需要对代码进行其他调整以适应这种方法。