交互媒体技术应用——2D横版冒险游戏初尝试

2023-11-21 20:20

本文主要是介绍交互媒体技术应用——2D横版冒险游戏初尝试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

2D横版冒险游戏——模拟人生《Just Die》

  • 想法
  • 背景
    • 使用工具
    • 参考资料
  • 关键技术
    • 小球的运动
    • 碰撞检测
    • 粒子系统
    • 优化算法
      • 粒子系统的预处理
      • 从预处理的粒子系统中获取需要的子系统
    • 其他
  • 后记
  • 附录

想法

最早是在B站看到一个视频,介绍了一个模拟人生的横版游戏,玩家需要操控游戏中的角色,根据不同的关卡做出不同的举动,来完成他的一生。简单但是很有深意,让我思考了很久,并在心里埋下了这个想法。这是一个就我个人而言代入感十分强烈的小Demo,包含我对这20年成长的一些思考,可能不有趣,而且由于代码能力不足,有一些糟糕的bug,导致游戏可玩性不强,以后应该会继续做,做出我满意的样子来。

背景

使用工具

processing

参考资料

《代码本色 The Nature of Code》 —— Daniel Shiffman
在这里插入图片描述
本Demo中参考的内容主要包含本书的前四章,向量、力、粒子系统等。

关键技术

小球的运动

将小球定义为包含位置(location)、速度(velocity)以及加速度(acceleration)的对象,根据加速度更新速度,根据速度更新位置,来实现小球的运动效果。这三个变量都存储在Processing自带的向量PVector中。

class people extends Collider {PVector velocity;  //速度PVector acceleration;  //加速度float topspeed=2; //最大速度float mass=10;  //质量float c=0.001;//摩擦力系数people() {location=new PVector(10, height-10);velocity=new PVector(0, 0);acceleration=new PVector(0, 0);R=5.0;}people(PVector location_, PVector velocity_, PVector acceleration_, float R_) {location=location_.get();velocity=velocity_.get();acceleration=acceleration_.get();R=R_;}}

在Update中更新变量,并在display绘制实时图像:

  void update() {applygravity();velocity.add(acceleration);location.add(velocity);velocity.limit(topspeed);acceleration=new PVector(0, 0);}void display() {fill(0);ellipse(location.x, location.y, 2*R, 2*R);}

这里面还包含了对力的应用addforce();我们知道,速度决定位置的变化,加速度决定速度的变化,而加速度来源于力,因此当我们想要改变一个物体的运动状态时,只需要给他加上一个力即可:

  void applyForce(PVector force) {PVector f=force.get();f.div(mass);acceleration.add(f);}

还可以细分为许多不同类型的力,如重力、摩擦力、弹力、万有引力等:

  void applyfriction() {PVector velocity_=velocity;velocity_.mult(-1);velocity_.normalize();PVector friction=velocity_.mult(c);applyForce(friction);}
  void applygravity() {PVector gravity=new PVector(0, 0.2);applyForce(gravity);}

(本Demo中虽然有计算摩擦力的函数,但是由于一些不知名Bug以及时间有限没用实际应用摩擦力,这里只做展示)

碰撞检测

其实前人已经做了十分完善的、包含碰撞的物理库函数Box2D(该库在代码本色的第五章中有详细介绍),但是由于作业要求用到三章的知识,所以我没用调用物理库而是自己实现了一个简陋的碰撞检测机制:

  Collederflag isCollision(Collider p) {float Mindis=R+p.R;if (abs(location.x-p.location.x)<=Mindis&&abs(location.y-p.location.y)<=Mindis) {if (location.x>=p.location.x) {//左侧撞击flag.horiflag=-1;} else if (location.x<p.location.x) {//右侧撞击flag.horiflag=1;}if (location.y<=p.location.y) {//下侧撞击flag.verflag=1; //   println("上方");} else if (location.y>p.location.y) {//上侧撞击flag.verflag=-1;//  println("下方");}return flag;} else {return flag;}}

原理大概就是根据计算每个物体的中心间的距离,根据这个距离与半径之间的关系来判断物体是否发生碰撞。在该Demo中根据两个碰撞物体的不同还有多个类似的碰撞检测函数,这里就不放了(其实只是因为本人代码能力不足,导致代码有些乱,也没用运用接口)。

粒子系统

在这里插入图片描述
在本Demo中存在各种不同的对象,我们将他们存储在粒子系统中。比如图片中作为边界和障碍物的砖块:
我们将他们定义为rectframe类,并在ArrayLIst frames中存储他们:

class rectframe extends frame {rectframe(PVector location_, float R_) {location=location_.get();R=R_;}void display() {rectMode(CENTER); stroke(255);strokeWeight(1);fill(0);rect(location.x, location.y, 2*R, 2*R);}
}
ArrayList<frame> frames = new ArrayList<frame>();

类似的还有图中的发射装置、发射出的子弹等,都是存储在粒子系统中的,以便于我们对一类对象进行相同的操作。

ArrayList<shootframe> shootframes = new ArrayList<shootframe>();
ArrayList<record> records = new ArrayList<record>();
ArrayList <moveblock> moveblocks=new ArrayList<moveblock>();

遍历粒子系统中的所有对象并在画面上绘制:

      for (frame f : frames) {f.display();}

优化算法

为了给小球施加力,我们需要判断小球是否与其他物体发生了碰撞,但是如果每帧都让小球对所有物体进行一次碰撞检测,这显然是不现实并且十分浪费的。所以我们需要进行一些优化,首先我们对所有数据预处理。

粒子系统的预处理

我们知道粒子系统就像一个能存储结构体的数组。而将数据存入粒子系统也很简单:

    List.add(f);

其中List 是ArrayList类型的对象,而f就是你要存储的数据。
但是只是存储是不够的,为了减少计算量,在每次存储新元素时,我们按照位置来插入元素,这里运用了之前在数据结构还是哪门课上学习的思路。这就好比现在给你一个乱序的数组[1,8,2,5,3,6,0,2],让你找一个大于3小于6的数,那么我们必须对每个元素进行判断,其是否大于3?其是否小于6?而如果给你一个排好序的数组[0,1,2,2,3,5,6,8],那么我们只需要从头开始判断,当我们找到数组中第一个大于3的数的位置,再从这个位置开始,找到第一个大于6的数的位置,这两个位置之间的数,就是我们需要的。但是每帧都对所有粒子系统排序显然效率也是很低的,所以我们只在生成对象,插入粒子系统的过程中,按照其x的坐标插入,就可以插入得到一个有序的粒子系统:

//---------------用于将新生成的frame对象插入ArrayList中
void insert(ArrayList List, frame f, int min, int max) {if (max==0) {//初始化List.add(f);return;}int mid=(max+min)/2;frame p=(frame)List.get(mid);//println(p.location.x);//println("mid:"+mid);//println("min:"+min);//println("max:"+max);frame pmin=(frame)List.get(min);frame pmax=(frame)List.get(max-1);//println("pmin:"+pmin.location.x);//println("pmax:"+pmax.location.x);if (f.location.x<pmin.location.x) {List.add(0, f);return;} else if (f.location.x>pmax.location.x) {List.add(max, f);return;} else if (f.location.x<=p.location.x) {frame pmid=(frame)List.get(mid-1);if (f.location.x>=pmid.location.x) {List.add(mid, f);return;}max=mid;insert(List, f, min, max);} else if (f.location.x>=p.location.x) {frame pmid=(frame)List.get(mid+1);if (f.location.x<=pmid.location.x) {List.add(mid+1, f);return;}min=mid;insert(List, f, min, max);}
}

从预处理的粒子系统中获取需要的子系统

然后当我们需要进行碰撞检测时,我们只需要根据阈值,从粒子系统中取出可能进行碰撞的子系统,对子系统中的粒子进行碰撞检测即可:

// 遍历数组,只取出ArrayList中有可能与YOU进行碰撞的对象进行碰撞检测
ArrayList<frame> findArrayList(ArrayList<frame> frames, Collider p) {ArrayList<frame> newframes=new ArrayList<frame>();float x=p.location.x;float R=p.R;float Up=x+maxR+R;float Down=x-maxR-R;//println("x:"+x);//  println("Up:"+Up);//    println("Down:"+Down);for (frame f : frames) {if (f.location.x>=Down&&f.location.x<=Up) {newframes.add(f);}}return newframes;
}

其他

其他还包括许多诸如开始界面的绘制、死亡提示、电影谢幕效果等,这里不放太多代码,具体直接看Demo就好:

void Gameover() {fill(0);rect(width/2, height/2, 3*width/4, height*3/4);textAlign(CENTER);fill(255);textSize(45);text("You Die", width/2, height/2);textSize(20);text("left click to continue", width/2, height/2+100);text("right click to reset", width/2, height/2+150);
}

后记

其实个人对这个Demo不是很满意,太过简陋的碰撞检测,以及还有许多想法都没有实现,对Processing和Java本身也不是很擅长,代码基础不足。以后应该会继续完善这个想法,附录放上完整的代码、可执行文件和《代码本色》PDF版。

附录

《代码本色 The Nature of Code》:链接:https://pan.baidu.com/s/1nxk5u4oIBDiEuUsOFi9Syw 密码:h0sv
《Just Die》可执行文件:链接:https://pan.baidu.com/s/1WXzLKgeGWmTb8dZENuoSBA 密码:kutl
《Just Die》源文件:链接:https://pan.baidu.com/s/1AtQ9K9eBw393o-9kyJHgoA 密码:lp6v

这篇关于交互媒体技术应用——2D横版冒险游戏初尝试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/404870

相关文章

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

Java中的登录技术保姆级详细教程

《Java中的登录技术保姆级详细教程》:本文主要介绍Java中登录技术保姆级详细教程的相关资料,在Java中我们可以使用各种技术和框架来实现这些功能,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录1.登录思路2.登录标记1.会话技术2.会话跟踪1.Cookie技术2.Session技术3.令牌技

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

Python Flask 库及应用场景

《PythonFlask库及应用场景》Flask是Python生态中​轻量级且高度灵活的Web开发框架,基于WerkzeugWSGI工具库和Jinja2模板引擎构建,下面给大家介绍PythonFl... 目录一、Flask 库简介二、核心组件与架构三、常用函数与核心操作 ​1. 基础应用搭建​2. 路由与参

Spring Boot中的YML配置列表及应用小结

《SpringBoot中的YML配置列表及应用小结》在SpringBoot中使用YAML进行列表的配置不仅简洁明了,还能提高代码的可读性和可维护性,:本文主要介绍SpringBoot中的YML配... 目录YAML列表的基础语法在Spring Boot中的应用从YAML读取列表列表中的复杂对象其他注意事项总

电脑系统Hosts文件原理和应用分享

《电脑系统Hosts文件原理和应用分享》Hosts是一个没有扩展名的系统文件,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应... Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应

CSS 样式表的四种应用方式及css注释的应用小结

《CSS样式表的四种应用方式及css注释的应用小结》:本文主要介绍了CSS样式表的四种应用方式及css注释的应用小结,本文通过实例代码给大家介绍的非常详细,详细内容请阅读本文,希望能对你有所帮助... 一、外部 css(推荐方式)定义:将 CSS 代码保存为独立的 .css 文件,通过 <link> 标签

Web技术与Nginx网站环境部署教程

《Web技术与Nginx网站环境部署教程》:本文主要介绍Web技术与Nginx网站环境部署教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Web基础1.域名系统DNS2.Hosts文件3.DNS4.域名注册二.网页与html1.网页概述2.HTML概述3.