安卓端回合制文字策略游戏开发
上网络程序设计这门课,课程要求做个基于android studio的安卓小游戏,要结合上海大学背景,所以做了这个小游戏,分享一下。虽然不好玩哈哈
开发环境
C:\Users\XXX>java --version
java 11.0.1 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)
Android studio里的设备是6.7寸的1k屏幕,安卓版本是12,API为31
玩法设计
为继承上大老校长钱伟长遗愿:
当今世界的大城市中,以城市的名字命名的大学有不少,其中也不乏佼佼者,上海大学的奋斗目标就是,经过若干年的努力,达到这些优秀大学的水平,与他们并驾齐驱
玩家扮演上大校长,每回合(一年)对上大进行操作,每轮进行的操作可以在学术声誉,雇主声誉,单位教员论文引文数,师生比例,国际教师比例,国际学生比例六个方面对学校造成影响,然后以40,10,20,20,5,5(qs标准)对学校发展进行评估。玩家每轮进行操作,操作会对qs单项得分发生变化,经过若干年的努力,使得圣代超过目标院校(暂定为北大)
游戏设计框架
本游戏主要由三个activity组成、六个游玩按钮、一个再来一局按钮、一个“金手指”按钮、以及一个记录游玩数据的文本框。其中Activity中包含主界面、金手指界面、以及物品界面共计三个界面。六个游玩按钮,点击后会在接下来的一年中进行不同的操作,通过更改不同的qs单项分数来实现对整体的qs分数会发生变化的目的。再来一局按钮在主界面中部下方,主要起到重置游戏数据到2022年的作用。“金手指”按钮在主界面的最下方,点击后进入金手指界面,通过点击金手指界面中的物品,会对qs单项分数直接进行改变。同时存在文本框,实时记录游玩数据。游戏设置背景音乐为上大校歌,最终胜利时会播放胜利音乐,并且胜利提示会在在文本框展示。游戏的整体设计框架如图所示。
如上图所示,当我们启动游戏时,直接进入主界面,开始播放背景音乐——《上海大学校歌》。主界面中有八个按钮,六个游玩按钮对应一年中发生的事件,分别是学科建设、学生政策、教师政策、学校政策、国际政策、学校建设。点击再来一局按钮后,时间回到2022年,上面的两所学校的qs分数也回滚到2022年的数据,点击金手指按钮,会进入金手指界面,点击物品列表的金手指,可以直接按百分比加成qs单项分数。
游戏界面介绍
游戏主页面
当我们通过Android Studio平台启动程序时,我们插入一张图片作为游戏开始背景,并绑定各种按键以及三个文本框,游戏开始界面展示如图
金手指页面
金手指界面中有一个image按钮,一个文本框,一个RecyclerView用于展示物品,金手指界面及代码展示如图
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_goldfinger);
//绑定控件以及设置监听
shengdai_info = findViewById(R.id.shengdai_info);
btn_return=findViewById(R.id.btn_return);
btn_return.setOnClickListener(btn_return_onclicklistener);
//打包SCHOOL类的shengdai
Intent intent =getIntent();
Bundle bundle = intent.getBundleExtra("bundle");
shengdai = bundle.getParcelable("shengdai");
// Log.d("opengold", String.valueOf(shengdai.goldFinger_list.size()));
shengdai_info.setText(shengdai.show_info());//更新圣代信息
goldfinger_view = findViewById(R.id.goldfinger_view);
GOLDFINGERADAPTER goldfingeradapter = new GOLDFINGERADAPTER(shengdai,shengdai_info);
goldfinger_view.setAdapter(goldfingeradapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
goldfinger_view.setLayoutManager(linearLayoutManager);
}
金手指物品界面
创建金手指物品,设置好图片、文本框的大小后,通过GoldFingerViewHolder类来实现各个物品中name、intro和图片的绑定。界面及代码展示如图
class GoldFingerViewHolder extends RecyclerView.ViewHolder{
TextView goldfinger_name;
TextView goldfinger_intro;
ImageView goldfinger_img;
//设置每个金手指对应的name、intro、和image图片
public GoldFingerViewHolder(View goldfingerView)
{
super(goldfingerView);
this.goldfinger_img = goldfingerView.findViewById(R.id.goldfinger_img);
this.goldfinger_name = goldfingerView.findViewById(R.id.goldfinger_name);
this.goldfinger_intro = goldfingerView.findViewById(R.id.goldfinger_intro);
}
}
游戏功能实现
类的创建
首先是在SCHOOL.java里创建一个SCHOOL类包含学校名称,qs单项分数和总分,目前的年份,还有六个金手指选项
public class SCHOOL implements Parcelable {
String name;
double academic_reputation;//学术声誉
double employer_reputation;//雇主声誉
double reference;//论文引用数
double teacher_student_ratio;//师生比例
double international_student_ratio;//国际学生比例
double international_teacher_ratio;//国际教师比例
double qs_score;//qs总分
int years=2022;
List<GoldFinger>goldFinger_list = new ArrayList<>(6);
创建两个对象,一个圣代,一个北大,选取2022年qs数据
shengdai = new SCHOOL("圣代",21.1,8.5,22.3,54.2,7,44.3);
beida = new SCHOOL("北大",99.3,96.5,96.7,87.3,36.9,57.1);
刷新信息
在SCHOOL.java写一个show_info()函数,qs总分通过计算qs单项得分得出,函数返回包含SCHOOL类信息的字符串,每次调用年份加1
String show_info()
{
qs_score = academic_reputation*0.4+employer_reputation*0.1+reference*0.2+teacher_student_ratio*0.2+international_student_ratio*0.05+international_teacher_ratio*0.05;
years++;
String info_temp="";
info_temp = "学校:"+ name +"\r\n";
info_temp = info_temp + "学术声誉:"+String.format("%.2f", academic_reputation)+"\r\n";
info_temp = info_temp + "雇主声誉:"+String.format("%.2f",employer_reputation)+"\r\n";
info_temp = info_temp + "每位教员引用率:"+String.format("%.2f",reference)+"\r\n";
info_temp = info_temp + "师生比:"+String.format("%.2f",teacher_student_ratio)+"\r\n";
info_temp = info_temp + "国际学生占比:"+String.format("%.2f",international_student_ratio)+"\r\n";
info_temp = info_temp + "国际教师占比:"+String.format("%.2f",international_teacher_ratio)+"\r\n";
info_temp = info_temp + "QS加权总分:"+String.format("%.2f",qs_score)+"\r\n";
return info_temp;
}
在Mainactivity.java里写个刷新信息函数,通过调用上一步里的show_info()函数来显示信息
private void refresh_info()
{
shengdai_info.setText(shengdai.show_info());
other_info.setText(beida.show_info());
}
绑定textview控件后,通过调用refresh_info()来更新textview里的信息
shengdai_info = findViewById(R.id.shengdai_info);
other_info = findViewById(R.id.other_info);
refresh_info();
把上大校歌的mp3文件以及胜利结算音乐放在res\raw文件夹中,然后创建背景音乐,设置循环播放,并开始播放背景音乐
//创建背景音乐
music_palyer = MediaPlayer.create(MainActivity.this,R.raw.shengdai_music);
music_palyer.setLooping(true);//循环播放
music_palyer.start();//开始播放
music_win = MediaPlayer.create(MainActivity.this,R.raw.ez4);
music_win.setLooping(true);//可有可无
把名为story_info的textview控件设置为能垂直拉动的滑动框
<TextView
android:id="@+id/story_info"
android:layout_width="344dp"
android:layout_height="172dp"
android:layout_marginTop="15dp"
android:maxLines="9999"
android:scrollbars="vertical"//竖直方向可以滑动
android:singleLine="false"
android:fadeScrollbars="false"//滑动框不消失
android:text=""
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.483"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
绑定textview控件并设置滑动方法,并显示欢迎文字,欢迎文字写在string然后调用(因为太长了,影响代码美感)
//更新story_info
story_info =findViewById(R.id.story_info);
story_info.setText(getString(R.string.stort_start));//获得游戏开始strings
story_info.setMovementMethod(ScrollingMovementMethod.getInstance());
玩法实现
六个玩法实现方式相似,先绑定按钮,设置监听onclicklistener。
//学科建设
btn_subject = findViewById(R.id.btn_subject);
btn_subject.setOnClickListener(subject_listener);
//学生建设
btn_student = findViewById(R.id.btn_student);
btn_student.setOnClickListener(onClickListener_for_student);
//教师建设
btn_teacher = findViewById(R.id.btn_teacher);
btn_teacher.setOnClickListener(onClickListener_for_teacher);
//学校政策
btn_change = findViewById(R.id.btn_change);
btn_change.setOnClickListener(onClickListener_for_change);
//国际政策
btn_international = findViewById(R.id.btn_international);
btn_international.setOnClickListener(onClickListener_for_international);
//学校建设
btn_build = findViewById(R.id.btn_build);
btn_build.setOnClickListener(onClickListener_for_build);
以学生建设为例,写一下onclicklistener监听,监听到点击后,调用学生政策函数返回字符串,然后在story_info显示信息,接着刷新学校qs信息,进行胜负判断,最后滑动框置底部。
//学生政策
View.OnClickListener onClickListener_for_student =new View.OnClickListener()
{
@Override
public void onClick(View view) {
String return_info = shengdai.student(beida);//调用学生政策函数返回字符串
story_info.setText(story_info.getText().toString()+"\r\n"+return_info);//在story_info上显示信息
refresh_info();
win();
story_info.scrollTo(0,story_info.getLayout().getHeight()-story_info.getHeight());//滑动框置底
}
};
写一下学生政策函数student(),使得点击后qs单项分数发生变化,up和down是0.5-2的两个随机数用于对qs单项分数进行改变。rand1和prop用于判断发生什么事件,temp用来显示两个学校的qs单项差距或者总差距。back_text 字符用于显示当年发展的结果,最终return,其他五个游玩按钮相似,只不过是更改不同的qs单项。
//学生政策
public String student(SCHOOL studented_school)
{
double up_dice,down_dice,prop,rand1,temp;
//up_dice,down_dice ------在50%-200%范围内变化
up_dice = Math.random()*0.04+0.01;//0-1随机数--->0.5-2
down_dice = Math.random()*0.04+0.01;
rand1 = Math.random();//用于点击后判断发生哪种事件
String back_text = "";//返回文字
prop = qs_score/(qs_score+studented_school.qs_score);//按照qs总分的比例进行判断发生哪种事件(0.3)
if(rand1>prop)//以圣代qs3分,北大qs7分,那么有70%的概率发生以下事件
{
employer_reputation=employer_reputation*(1+up_dice)-studented_school.employer_reputation*down_dice*0.01;
temp=Math.abs(studented_school.employer_reputation-employer_reputation);
back_text =years+"年"+name +"经过一年的奋斗,提高了雇主声誉,达到了"+String.format("%.2f",employer_reputation)+"。与"+studented_school.name+"的雇主声誉仍有"+String.format("%.2f",temp)+"的差距";
}
else//有30%的概率发生以下事件
{
studented_school.employer_reputation=studented_school.employer_reputation*(1+down_dice)-employer_reputation*up_dice*0.1;
temp=Math.abs(studented_school.employer_reputation-employer_reputation);
back_text = years+"年"+studented_school.name+"看到"+name+"想要进行学科建设,受到了刺激,雇主声誉达到了"+String.format("%.2f",studented_school.employer_reputation)+"。与圣代的差距增大到了"+String.format("%.2f",temp);
}
return back_text;
}
胜负判断的实现
win()函数的作用是通过调用SCHOOL.java里的winorloss()函数通过比较两所学校的QS总分来判断是否胜利,若胜利,则停止原背景音乐,播放胜利音乐,选取的是EZ4ENCE
//展示获胜,并播放胜利音乐
private void win()
{
String return_inf =shengdai.winorloss(beida);
story_info.setText(story_info.getText().toString()+"\r\n"+return_inf);//是否胜利返回结果
if(shengdai.qs_score>beida.qs_score) {
music_palyer.pause();
music_win.start();
}
}
``
```java
//获胜音乐
music_win = MediaPlayer.create(MainActivity.this,R.raw.ez4);
music_win.setLooping(true);
winorloss()函数如下
//判断是否胜利
public String winorloss(SCHOOL school2){
int years_temp;
String winorlose_text = "";
if(qs_score>school2.qs_score){
years_temp=years-2022;
winorlose_text = "\n\n\n\n\n\n\n\n在"+years+"年,圣代的QS排名终于超过了北大。共耗时"+years_temp+"年。\n\n\n";
}
else{
winorlose_text = "\n";
}
return winorlose_text;
}
再来一局的实现
为了实现“再来一局”,先对按键进行绑定,并设置监听
//下一局
btn_next= findViewById(R.id.btn_next);
btn_next.setOnClickListener(onClickListener_for_next);
开始写onclicklistener,重新给两个对象赋值为2022年的QS分数,并输出提示文字“重生到2022年,继续开始你的圣代校长之路吧”,停止获胜音效(如果有),重新开始背景音乐,滑动框置底。
//再来一局
View.OnClickListener onClickListener_for_next =new View.OnClickListener() {
@Override
public void onClick(View view) {
shengdai = new SCHOOL("圣代",21.1,8.5,22.3,54.2,7,44.3);
beida = new SCHOOL("北大",99.3,96.5,96.7,87.3,36.9,57.1);
music_win.pause();
refresh_info();
story_info.setText(story_info.getText().toString()+"\n\n\n\n\n\n\n\n\n\n\n\n\n\n重生到2022年,继续开始你的圣代校长之路吧\r\n");
story_info.scrollTo(0,story_info.getLayout().getHeight()-story_info.getHeight());
music_palyer.start();
}
};
“金手指”按钮设置
设置imagebutton的背景为透明,设置图片为res\mipmap的goldfinger.jpg图片
<ImageButton
android:id="@+id/btn_glodfinger"
android:layout_width="163dp"
android:layout_height="148dp"
android:layout_marginTop="16dp"
android:background="#00000000"//透明
android:rotation="0"
android:rotationX="0"
android:rotationY="0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_next"
app:srcCompat="@mipmap/goldfinger"//调用图片
/>
绑定按键,并设置监听
//跳转到打开金手指
btn_goldfinger=findViewById(R.id.btn_glodfinger);
btn_goldfinger.setOnClickListener(onClickListener_for_opengoldfinger);
然后将“shengdai”类打包传到goldfingeractivity里
//打开金手指
View.OnClickListener onClickListener_for_opengoldfinger =new View.OnClickListener() {
@Override
public void onClick(View view) {
//Log.d("openstudent:","From on click_open_studentpolicy");
Intent intent =new Intent(MainActivity.this,GoldfingerActivity.class);
Bundle bundle = new Bundle();
bundle.putParcelable("shengdai",shengdai);
intent.putExtra("bundle",bundle);
startActivity(intent);
}
};
“金手指”界面的实现
在goldfingeractivity中,有三个控件,一个是最上面的RecyclerView展示物品 ,一个textview展示shengdai的qs信息,一个Imagebutton用于返回mainactivity。
先绑定textview控件和imagebutton控件,然后设置返回按钮的监听
//绑定控件以及设置监听
shengdai_info = findViewById(R.id.shengdai_info);
btn_return=findViewById(R.id.btn_return);
btn_return.setOnClickListener(btn_return_onclicklistener);
尝试把goldfingeractivity更新的shengdai信息传回mainactivity(事实是最后没做成,只要在Mainactivity里写接收goldfingeractivity里传来的信息,程序就无法启动)
class BTN_RETURN_ONCLICK implements View.OnClickListener
{
@Override
public void onClick(View v) {
Intent intent = new Intent(GoldfingerActivity.this,MainActivity.class);
startActivity(intent);
//尝试把数据传回去
Bundle bundle = new Bundle();
bundle.putParcelable("shengdai",shengdai);
intent.putExtra("bundle",bundle);
finish();
startActivity(intent);
}
}
BTN_RETURN_ONCLICK btn_return_onclicklistener = new BTN_RETURN_ONCLICK();
创建一个金手指物品的类,包含名字,介绍,类型,占用状态、对应图片,以及可以增加的各项属性
public class GoldFinger implements Parcelable {
String name;//名称
String intro;//介绍
int type;//类型
int occupied;//占用状态
int img_item;//对应图片
//以下都是为了增加
double academic_reputation_add;//学术声誉
double employer_reputation_add;//雇主声誉
double reference_add;//论文引用数
double teacher_student_ratio_add;//师生比例
double international_student_ratio_add;//国际学生比例
double international_teacher_ratio_add;//国际教师比例
写一个use()函数来实现使用金手指的作用,具体的算法就是简单的在原有基础上乘系数
public void use(SCHOOL school)
{
school.academic_reputation=school.academic_reputation*(1+this.academic_reputation_add);
school.employer_reputation=school.employer_reputation*(1+this.employer_reputation_add);
school.teacher_student_ratio=school.teacher_student_ratio*(1+this.teacher_student_ratio_add);
school.reference=school.reference*(1+this.reference_add);
school.international_teacher_ratio=school.international_teacher_ratio*(1+this.international_teacher_ratio_add);
school.international_student_ratio=school.international_student_ratio*(1+this.international_student_ratio_add);
}
六个金手指的名称和介绍以及作用如下,以最后一个“夏季学期”为例,name就是“夏季学期”,intro是“校长倡导开展夏季学期,使得学校国际教师增加160%”,目前只有一种类型的金手指,所以type设置为0,占用状态occupied默认为0,最后一项的international_teacher_ratio_add为1.6,作用就是使得QS单项中的国际教师比例分数增加。
public SCHOOL(String name_temp,double academic_reputation_temp,double employer_reputation_temp,double reference_temp,double teacher_student_ratio_temp,double international_student_ratio_temp,double international_teacher_ratio_temp)
{
name=name_temp;
academic_reputation=academic_reputation_temp;
employer_reputation=employer_reputation_temp;
teacher_student_ratio=teacher_student_ratio_temp;
reference=reference_temp;
international_teacher_ratio=international_teacher_ratio_temp;
international_student_ratio=international_student_ratio_temp;
goldFinger_list.add(new GoldFinger("学术先锋","校长作为学术先锋,对学校学术声誉增加20%",0,0,R.mipmap.xueshu,0.2,0.0,0.0,0.0,0.0,0.0));
goldFinger_list.add(new GoldFinger("实业兴邦","校长倡导实业兴邦,对学校雇主声誉增加80%",0,0,R.mipmap.gongdi,0.0,0.8,0.0,0.0,0.0,0.0));
goldFinger_list.add(new GoldFinger("科技强国","校长紧跟国家号召,倡导科技强国,对学校教员引文数量增加40%",0,0,R.mipmap.keji,0.0,0.0,0.4,0.0,0.0,0.0));
goldFinger_list.add(new GoldFinger("小班教育","校长倡导小班教育,对学校师生比增加40%",0,0,R.mipmap.xiaoban,0.0,0.0,0.0,0.4,0.0,0.0));
goldFinger_list.add(new GoldFinger("一带一路","校长为国家一带一路做贡献,援非建非,使得国际学生增加160%",0,0,R.mipmap.fupin,0.0,0.0,0.0,0.0,1.6,0.0));
goldFinger_list.add(new GoldFinger("夏季学期","校长倡导开展夏季学期,使得学校国际教师增加160%",0,0,R.mipmap.guoji,0.0,0.0,0.0,0.0,0.0,1.6));
}
“金手指”UI实现
金手指的UI通过Adapter来实现
先编写一个列表项目每一项的布局:
<!-- 定义一个用于显示金手指的ImageView -->
<ImageView
android:id="@+id/goldfinger_img"
android:layout_width="120dp"
android:layout_height="120dp"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<!-- 把金手指的名称写出来 -->
<TextView
android:id="@+id/goldfinger_name"
android:layout_width="238dp"
android:layout_height="60dp"
android:gravity="center"
android:text="TextView"
android:textAlignment="center"
android:textSize="25dp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/goldfinger_img"
tools:layout_editor_absoluteY="3dp" />
<!-- 显示金手指的介绍 -->
<TextView
android:id="@+id/goldfinger_intro"
android:layout_width="235dp"
android:layout_height="60dp"
android:text="TextView"
app:layout_constraintStart_toEndOf="@+id/goldfinger_img"
app:layout_constraintTop_toBottomOf="@+id/goldfinger_name" />
通过写一个goldfingerviewholder类来实现每个金手指的name、intro、和image图片的绑定
class GoldFingerViewHolder extends RecyclerView.ViewHolder{
TextView goldfinger_name;
TextView goldfinger_intro;
ImageView goldfinger_img;
//设置每个金手指对应的name、intro、和image图片
public GoldFingerViewHolder(View goldfingerView)
{
super(goldfingerView);
this.goldfinger_img = goldfingerView.findViewById(R.id.goldfinger_img);
this.goldfinger_name = goldfingerView.findViewById(R.id.goldfinger_name);
this.goldfinger_intro = goldfingerView.findViewById(R.id.goldfinger_intro);
}
}
“金手指”使用的实现
先设置如何得到标签
@Override
public void onBindViewHolder(@NonNull GoldFingerViewHolder holder, int position) {
GoldFinger goldfinger = school.goldFinger_list.get(position);
//设置img、name、intro
holder.goldfinger_img.setImageResource(goldfinger.img_item);
holder.goldfinger_name.setText(goldfinger.name);
holder.goldfinger_intro.setText(goldfinger.intro);
//setOnclickListener
holder.goldfinger_img.setTag(position);
holder.goldfinger_img.setOnClickListener(goldfinger_click_listener);
}
设置监听,点击后得到标签,调用刚才讲的use()函数来使用对应标签的金手指,然后通过调用school.java里的show_info()函数刷新名为school_info的textview中的shengdai信息,当判定type=0时,删除该金手指(后续可以做更多类型的金手指)
class GOLDFINGER_CLICK_LISTENER implements View.OnClickListener{
@Override
public void onClick(View view) {
int position=(int)view.getTag();
school.goldFinger_list.get(position).use(school);//使用金手指
school_info.setText(school.show_info());//调用school里的show_info()函数刷新信息
//type=0的一次性金手指,用完即删掉
if(school.goldFinger_list.get(position).type == 0)
{
//点击后去除
school.goldFinger_list.remove(position);
notifyItemRemoved(position);
notifyDataSetChanged();
}
}
}
GOLDFINGER_CLICK_LISTENER goldfinger_click_listener= new GOLDFINGER_CLICK_LISTENER();
#总结与改进
音乐会重复播放,目前的打算是增加一个imagebutton,给它设置个监听,目的是当点击后,停止所有音乐
View.OnClickListener onClickListener_for_stopmsc =new View.OnClickListener()
{
@Override
public void onClick(View view) {
story_info.setText(story_info.getText().toString()+"\r\n音乐停止");
//功能实现
do {
music_player.pause()
music_win.pause()
}while(music_player&&music_win!=0);//伪代码,我没试验过
story_info.scrollTo(0,story_info.getLayout().getHeight()-story_info.getHeight());//滑动框置底
}
};
还存在一个问题是从goldfingeractivity里传不回shengdai的信息,我想的解决方法是再做一个新的main2activity完全复制mainactivity里面的页面,只不过把金手指的imagebutton给删掉,同时在mainactivity传intent的时候把beida对象的信息也传过去,最终在新的main2activity中重新创建两个新的对象,来避免程序打不开的情况。
程序获取
上传到了github里
https://github.com/JzH6666/–Java-Android-Game–