一、项目简介
系统简介
游戏集合平台,解决多游戏资源管理、用户社交(好友)功能整合的问题,采用 Java Swing(游戏界面)、文件 IO(资源加载)、自定义类(游戏逻辑)等技术,实现推箱子、弹弹堂等游戏的集成与好友互动。
二、个人任务简述
1、用户交互功能模块
实现登录(log_in_interface)、注册(register_interface)、个人信息(person_info)等界面,支持平台基础操作。
2、社交互动功能模块
完成好友搜索(search_friends)、好友请求(friendsRequest)、好友列表(friend_column)管理,实现用户社交关联。
3、游戏娱乐功能模块
集成推箱子(Box_Pusher)、弹弹堂(DDtank)等游戏,完成游戏资源加载(如 DDtank_material)与玩法逻辑实现。
4、聊天通讯功能模块
基于 ChatClient、ChatServer 实现客户端 - 服务端聊天交互,搭配 JIMSendTextPane 完成消息发送面板功能。
5 数据访问功能模块 采用 DAO 模式(DAO 包)管理数据库读写,通过 data_base、user_data 实现用户数据的存储与访问。
5、游戏联机功能
本项目的联机功能基于Java Socket实现客户端与服务端的双向通信,构建了一套稳定的实时对战与社交互动体系,支持多玩家跨主机接入。
三、个人负责功能详解
1 用户交互功能模块

功能1:界面组件布局与交互元素初始化
java
private void addComponents() {
// 用户名标签与输入框
JLabel lblNickname = new JLabel("Nickname:");
lblNickname.setBounds(100, 50, 113, 19);
contentPane.add(lblNickname);
lblNickname.setForeground(new Color(0, 0, 255));
lblNickname.setHorizontalAlignment(SwingConstants.CENTER);
lblNickname.setFont(new Font("宋体", Font.PLAIN, 16));
textField = new JTextField();
textField.setBounds(205, 50, 126, 30);
contentPane.add(textField);
textField.setColumns(20);
// 密码标签与密码输入框
JLabel lblPassword = new JLabel("Password:");
lblPassword.setBounds(100, 95, 113, 19);
contentPane.add(lblPassword);
lblPassword.setForeground(new Color(0, 0, 255));
lblPassword.setHorizontalAlignment(SwingConstants.CENTER);
lblPassword.setFont(new Font("宋体", Font.PLAIN, 16));
passwordField = new JPasswordField();
passwordField.setBounds(205, 95, 126, 30);
contentPane.add(passwordField);
// 回车键快捷登录绑定
passwordField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER)
log_in();
}
});
passwordField.setColumns(20);
// 登录按钮
JButton btnLogIn = new JButton("Log in");
btnLogIn.setBounds(170, 150, 158, 30);
contentPane.add(btnLogIn);
btnLogIn.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
log_in();
}
});
btnLogIn.setFont(new Font("宋体", Font.PLAIN, 16));
btnLogIn.setForeground(new Color(0, 0, 255));
// 注册跳转按钮
JButton btnRegisterNow = new JButton("Register Now!");
btnRegisterNow.setBounds(170, 190, 158, 30);
contentPane.add(btnRegisterNow);
btnRegisterNow.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
register_interface reg = new register_interface();
reg.setVisible(true);
}
});
btnRegisterNow.setForeground(new Color(255, 0, 255));
btnRegisterNow.setFont(new Font("宋体", Font.PLAIN, 16));
}
- 视觉交互优化:通过设置标签、按钮的字体、颜色与对齐方式,提升界面可读性,避免原生Swing组件的单调感,给用户更舒适的视觉体验;
功能2:账号密码有效性校验
java
/**
* 检查登陆是否成功
* @param name 用户名
* @param password 密码
* @return 0-账号/密码为空;-1-登录成功;1-登录失败(用户不存在或密码错误)
*/
int login_succ(String name,String password) {
int tl = name.length(), pl = password.length();
// 1. 账号或密码为空校验
if(tl == 0 || pl == 0)
return 0;
// 2. 调用数据库工具类,获取用户信息
Map<String, Object> userInfo = data_base.queryUserByUsername(name);
// 3. 判断用户是否存在
if (userInfo.isEmpty()) {
return 1; // 用户不存在,登录失败
}
// 4. 提取数据库中的密码,对比输入密码
String dbPassword = (String) userInfo.get("***");
if (dbPassword != null && dbPassword.equals(password)) {
return -1; // 密码匹配,登录成功
} else {
return 1; // 密码不匹配,登录失败
}
}
分层校验设计:采用"用户是否存在→密码是否匹配"的分层校验逻辑,既保障了校验的严谨性,也便于后续扩展更细致的错误提示(如区分"用户不存在"与"密码错误");
功能3:登录核心流程与交互反馈
java
void log_in() {
// 获取用户登陆信息并去空格,避免无效输入
String name = textField.getText().trim();
@SuppressWarnings("deprecation")
String password = passwordField.getText().trim();
// 账号密码有效性校验
if(login_succ(name, password) == -1) {
user_data dao = new user_data();
person_info user=new person_info();
user = dao.getUserByName(name);
// 尝试创建客户端并与服务端建立连接
ChatClient clientSocket = new ChatClient("172.19.76.57");
if(clientSocket.getclientSocket() != null && clientSocket.start(name)) {
// 登录成功,跳转至主界面
try {
platform_interface platform = new platform_interface(clientSocket, user);
platform.setVisible(true);
exit_(); // 关闭当前登录界面
} catch (Exception e1) {
e1.printStackTrace();
}
}
else if(clientSocket.getclientSocket() == null) {
// 网络连接失败提示
JOptionPane.showMessageDialog(null, " Connect refused!\n Please check your Internet connection! ",
" Internet Error ", JOptionPane.ERROR_MESSAGE);
}
else {
// 账号已在线提示
JOptionPane.showMessageDialog(null, " Account already logged in!\nPlease check the "
+ "security of your account! ", " Log In Error ", JOptionPane.ERROR_MESSAGE);
}
}
// 账号密码校验失败提示
else {
JOptionPane.showMessageDialog(null, " Log in failed!\n Please check your account! ",
" Log in Error ", JOptionPane.ERROR_MESSAGE);
}
}
网络连接校验:在账号密码校验通过后,额外校验与服务端的连接状态,兼顾了本地数据有效性与远程服务可用性,保障用户登录后的正常使用。
功能4:背景图片加载与异常兜底
java
// 加载图片的辅助方法
private ImageIcon loadImageIcon(String path) {
try {
// 1. 优先从classpath加载(适配jar打包场景)
URL resource = log_in_interface.class.getClassLoader().getResource(path);
if (resource != null) {
BufferedImage img = ImageIO.read(resource);
return new ImageIcon(img);
}
// 2. 类路径加载失败,尝试本地文件系统加载
File imageFile = new File(path);
if (imageFile.exists()) {
BufferedImage img = ImageIO.read(imageFile);
return new ImageIcon(img);
}
// 3. 尝试常见路径变体,提升加载成功率
String[] alternativePaths = {
"./images/" + path,
"images/" + path,
"./Chaplay/images/" + path
};
for (String altPath : alternativePaths) {
imageFile = new File(altPath);
if (imageFile.exists()) {
BufferedImage img = ImageIO.read(imageFile);
return new ImageIcon(img);
}
}
System.err.println("无法找到图片: " + path);
return new ImageIcon(); // 返回空图标
} catch (IOException e) {
System.err.println("加载图片时发生错误: " + path + ", 错误: " + e.getMessage());
return new ImageIcon(); // 返回空图标
}
}
提升界面美观度:通过背景图片加载,摆脱原生Swing界面的单调感,提升用户的视觉体验,让登录界面更具亲和力。
2 社交互动功能模块

基于Java Swing的好友搜索与申请功能实现解析
在社交互动模块中,好友搜索与申请是建立新好友关系的核心入口,search_friends 类实现了用户名搜索、用户有效性校验、好友申请发送三大核心功能,本文选取其中关键代码片段,拆解其交互逻辑与设计亮点。
用户有效性校验
java
//判断名字是否在数据库当中(封装数据库查询,废弃ResultSet)
public boolean friendsFind(String name) {
// 1. 调用data_base静态方法,获取封装后的用户信息Map
Map<String, Object> userInfo = data_base.queryUserByUsername(name);
// 2. 通过Map是否为空判断用户是否存在(无需处理ResultSet,无SQLException)
// 空Map表示未查询到用户,非空表示用户存在
return !userInfo.isEmpty();
}
解耦数据库操作 :不直接在社交交互模块编写JDBC与ResultSet处理逻辑,而是调用data_base工具类的queryUserByUsername()方法,将数据访问层与业务交互层分离,降低模块耦合度,提升代码可维护性;
好友申请发送
java
JButton btnSendingRequest = new JButton("Sending request!");
//发送好友申请
btnSendingRequest.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
String name = textField.getText();
// 场景1:输入用户名为空
if(name.length() == 0) {
JOptionPane.showMessageDialog(null, " Name empty!\n Please input your friends name! ",
" Input Error ", JOptionPane.ERROR_MESSAGE);
}
// 场景2:该用户已为当前用户好友
else if(platform_interface.myInfo.getfriends_nameall().indexOf(name)>=0) {
JOptionPane.showMessageDialog(null, " User " + name + " has been your friend!\n Please check your input! ",
" Input Error ", JOptionPane.ERROR_MESSAGE);
}
// 场景3:用户存在,发送好友申请
else if(friendsFind(name)) {
try {
// 发送申请格式:@friends request from + 当前用户名(与服务端约定格式)
platform_interface.clientSocket.sendMsg(name,
new String("@friends request from ") + platform_interface.myInfo.getname());
JOptionPane.showMessageDialog(null, " Request sent successfully!\n Please wait for your friends to "
+ "confirm! ",
" Request sent successfully ", JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e1) {
e1.printStackTrace();
}
}
// 场景4:用户不存在
else {
JOptionPane.showMessageDialog(null, " User " + name + " not found!\n Please check your input! ",
" Input Error ", JOptionPane.ERROR_MESSAGE);
}
}
});
约定统一的消息格式 :发送@friends request from + 用户名格式的消息,与服务端约定统一的通信协议,便于服务端解析并转发好友申请,是跨客户端交互的核心保障;
- 复用全局客户端实例 :调用
platform_interface.clientSocket.sendMsg()发送申请,复用已建立的Socket连接,无需重新创建连接,提升通信效率,同时保障连接的统一性;
好友请求窗口(friendsRequest)------ 待处理申请列表呈现
friendsRequest 类是独立的好友请求窗口,负责加载并展示所有待处理的好友申请,是用户处理好友关系的核心界面。
java
public class friendsRequest extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel contentPane;
private JPanel scrollContent; // 滚动面板的内容面板
/**
* Create the frame.
*/
public friendsRequest() {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(100, 100, 400, 360);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
// 1. 创建滚动面板,支持大量申请时滚动查看
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
// 2. 创建滚动面板内容面板,设置垂直流式布局
scrollContent = new JPanel();
scrollContent.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 5)); // 垂直排列,间距5
// 3. 将内容面板绑定到滚动面板,解决组件无法滚动问题
scrollPane.setViewportView(scrollContent);
// 4. 遍历等待列表,加载所有待处理好友申请
for(int i = 0; i < platform_interface.waitingList.size(); ++i) {
// 获取申请人姓名
String name = platform_interface.waitingList.get(i);
// 5. 调用数据库工具类,查询申请人详细信息
Map<String, Object> userInfo = data_base.queryUserByUsername(name);
// 6. 提取头像编号,设置默认值避免空指针
int pic = 0;
if (!userInfo.isEmpty()) {
pic = (Integer) userInfo.get("pic");
}
// 7. 创建请求项组件,添加到内容面板
requestItem request = new requestItem(pic, name);
scrollContent.add(request);
}
// 8. 刷新界面,确保组件正常显示
scrollContent.revalidate();
scrollContent.repaint();
}
}
功能实现与设计亮点解析
界面刷新机制 :通过revalidate()与repaint()刷新内容面板,确保申请项组件正常渲染,避免组件添加后无法显示的问题。
3、游戏娱乐功能模块

消消乐
消除判断(三消核心逻辑)
java
//判断是否有3个以上的连续的相同方块按钮
//kinds[0]为1表示横向消除,10表示纵向消除 11 表示横纵都消除
private boolean isThreeLinked(int y, int x, int[] kinds) {
int tmp;
int linked = 1;
kinds[0] = 0;
// 横向连续判断
if (x + 1 < matrixC) {
tmp = x + 1;
while (tmp < matrixC && pictures[y][x] == pictures[y][tmp]) {
linked++;
tmp++;
}
}
if (x - 1 >= 0) {
tmp = x - 1;
while (tmp >= 0 && pictures[y][x] == pictures[y][tmp]) {
linked++;
tmp--;
}
}
// 如果横向有超过三个连续块,标记为横向消除
if (linked >= 3) {
kinds[0] += 1;
}
// 纵向连续判断
linked = 1;
if (y + 1 < matrixR) {
tmp = y + 1;
while (tmp < matrixR && pictures[y][x] == pictures[tmp][x]) {
linked++;
tmp++;
}
}
if (y - 1 >= 0) {
tmp = y - 1;
while (tmp >= 0 && pictures[y][x] == pictures[tmp][x]) {
linked++;
tmp--;
}
}
// 如果纵向连续相同超过三个方块,标记为纵向消除
if (linked >= 3) {
kinds[0] += 10;
}
if (kinds[0] == 0)
return false;
return true;
}
图片交换与消除动画(游戏交互与视觉反馈)
图片交换逻辑(用户操作响应)
java
//交换图片
protected void swapPictures(int x, int y) {
if ((x >= minX && x <= maxX) && (y >= minY && y <= maxY)) {
int cAbs, rAbs;
cAbs = (x - minX) / PICTURE_WIDTH;
rAbs = (y - minY) / PICTURE_HEIGHT;
if (!isDoubleClicked) {
// 第一次点击,记录选中图片坐标
isDoubleClicked = true;
cCur = cAbs;
rCur = rAbs;
} else {
// 第二次点击,判断是否为相邻图片
isDoubleClicked = false;
if ((1 == Math.abs(rCur - rAbs) && cCur == cAbs)
|| (1 == Math.abs(cCur - cAbs) && rCur == rAbs)) {
// 记录交换前后坐标,触发交换动画
sr = rCur;
sc = cCur;
dr = rAbs;
dc = cAbs;
isExchanging = true;
isAnimation = true;
animation.start();
}
}
}
}
爆炸消除动画(视觉反馈)
java
private void callAnimation() {
for (int i = 0; i < matrixR; i++)
for (int j = 0; j < matrixC; j++)
if (pictures[i][j] == EMPTY)
explosion[i][j] = true;
isExplosion = true;
isAnimation = true;
animation.start();
}
class Animation implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (isExplosion) {
explosionCur++;
if (explosionCur >= explosionCnt) {
// 动画播放完毕,重置状态
explosionCur = 0;
for (int i = 0; i < matrixR; i++)
for (int j = 0; j < matrixC; j++)
explosion[i][j] = false;
isExplosion = false;
animation.stop();
}
repaint(); // 重绘界面,呈现动画帧
}
}
}
图片下落与补位(消除后流程衔接)
该方法实现消除后上方图片下落、空位置补新图的逻辑,是衔接单次消除与后续游戏流程的关键。
java
//图片下降
private void downPictures() {
int tmp;
for (int i = matrixR - 1; i >= 0; i--) {
for (int j = 0; j < matrixC; j++) {
if (pictures[i][j] == EMPTY) {
// 遍历上方图片,寻找非空图片下落补位
for (int k = i - 1; k >= 0; k--) {
if (pictures[k][j] != EMPTY) {
tmp = pictures[k][j];
pictures[k][j] = pictures[i][j];
pictures[i][j] = tmp;
break;
}
}
}
}
}
// 为空位置补充新图片
initPictureMatrix();
isFalling = false;
repaint();
// 检测连锁反应,触发连续消除
globalSearch(2);
}
推箱子

多关卡地图工厂(地图数据管理)
java
static class MapFactory
{
static byte sampleMap[][][] = {
{
{ 0, 0, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 4, 1, 0, 0, 0 },
{ 0, 0, 1, 9, 1 ,1 ,1, 1 },
// 省略部分地图数据...
},
{
{ 1, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 1, 9, 9, 5, 1, 0, 0, 0, 0 },
// 省略部分地图数据...
},
{
{ 0, 0, 1, 1, 1, 1, 0, 0 },
{ 0, 0, 1, 9, 9, 1, 1, 1 },
// 省略部分地图数据...
}
};
static int count = sampleMap.length;
public static byte[][] getMap(int grade)
{
byte temp[][];
if (grade >= 0 && grade < count)
temp = sampleMap[grade];
else //默认选第一关
temp = sampleMap[0];
int row = temp.length;
int column = temp[0].length;
byte[][] result = new byte[row][column];
for (int i = 0; i < row; i++)
for (int j = 0; j < column; j++)
result[i][j] = temp[i][j];
return result;
}
public static int getCount()
{
return count;
}
}
人物与箱子移动逻辑(游戏核心交互)
以moveUp()方法为例,拆解人物移动与箱子推动的核心逻辑,这是推箱子游戏的核心交互实现,直接决定游戏的可玩性与逻辑严谨性。
java
private void moveUp()
{
// 前方是墙壁,无法移动
if (map[row - 1][column] == WALL)
return;
// 前方是BOX或BOXONEND(箱子在目标点上)
if (map[row - 1][column] == BOX || map[row - 1][column] == BOXONEND)
{
// 检查箱子前方是否可移动(目标点或草地)
if (map[row - 2][column] == END || map[row - 2][column] == GRASS)
{
// 保存当前地图状态,用于悔棋
Map currMap = new Map(row, column, map);
list.add(currMap);
// 判断箱子移动后的状态(是否在目标点上)
byte boxTemp = map[row - 2][column] == END? BOXONEND : BOX;
// 判断人物移动后的状态(是否在目标点上)
byte manTemp = map[row - 1][column] == BOX? MANUP : MANUPONEND;
// 执行移动:箱子前移两步,人物前移一步
map[row - 2][column] = boxTemp;
map[row - 1][column] = manTemp;
// 恢复人物原位置的状态(草地或目标点)
map[row][column] = grassOrEnd(map[row][column]);
// 更新人物坐标
row--;
}
}
// 前方是GRASS(草地)或END(目标点),仅人物移动
else
{
if (map[row - 1][column] == GRASS || map[row - 1][column] == END)
{
// 保存当前地图状态,用于悔棋
Map currMap = new Map(row, column, map);
list.add(currMap);
// 判断人物移动后的状态
byte temp = map[row - 1][column] == END? MANUPONEND : MANUP;
map[row - 1][column] = temp;
// 恢复人物原位置的状态
map[row][column] = grassOrEnd(map[row][column]);
row--;
}
}
}
悔棋功能
java
public void undo()
{
if (acceptKey)
{
if (list.size() > 0)
{
// 获取最后一次保存的地图状态
Map priorMap = (Map) list.get(list.size() - 1);
// 恢复地图数据与人物坐标
map = priorMap.getMap();
row = priorMap.getManX();
column = priorMap.getManY();
// 重绘界面,呈现悔棋后的状态
repaint();
// 移除已撤销的历史状态,避免重复悔棋
list.remove(list.size() - 1);
}
else
{
DisplayToast("不能再撤销了!");
}
}
else
{
DisplayToast("此关已完成,不能撤销!");
}
}
// 鼠标右键触发悔棋
@Override
public void mousePressed(MouseEvent e)
{
if (e.getButton() == MouseEvent.BUTTON3)
{
undo();
}
}
- 基于历史记录的恢复 :通过
ArrayList存储每次有效移动的地图状态,采用"先进后出"的栈式结构,实现最近操作优先撤销,符合用户的操作习惯; - 状态锁控制 :通过
acceptKey标识控制
4、聊天通讯功能模块

聊天消息发送逻辑
java
send_msg_1.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
// 点击Ctrl键发送消息(注:代码中注释写"回车",实际是Ctrl键)
if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
// 检查是否选择了有效好友
if (fList.isEmpty() || friend_current < 0 || friend_current >= fList.size()) {
JOptionPane.showMessageDialog(null, "Please select a friend to chat with!", "No Friend Selected", JOptionPane.WARNING_MESSAGE);
return;
}
String msg = send_msg_1.getText();
// 本地聊天记录存储
fList.get(friend_current).msgList.add(msg);
fList.get(friend_current).usrList.add(1); // 标记为自己发送的消息
try {
// 将消息写入本地聊天记录文件
fileList.get(friend_current).write(new String(1 + ":" + msg + "\r\n").getBytes());
} catch (IOException e1) {
e1.printStackTrace();
}
// 界面添加消息气泡
addTextMessage(msg, 1, myInfo.getpic(), fList.get(friend_current).msgList.size());
send_msg_1.setText(""); // 清空输入框
// 通过客户端发送消息给服务端(转发给好友)
clientSocket.sendMsg(fList.get(friend_current).getname(), msg);
}
}
});
- 合法性校验:检查是否选择了好友,避免无目标时发送消息;
- 本地数据存储 :把消息存到当前好友的聊天记录列表(
msgList),并用usrList标记消息来源(1代表自己发送); - 本地文件持久化 :将消息写入本地聊天记录文件(
fileList对应好友的日志文件),保障聊天记录不丢失; - 界面与网络同步 :在聊天窗口添加消息气泡,清空输入框,同时调用
clientSocket.sendMsg将消息发送给服务端,由服务端转发给目标好友。
5、游戏联机功能


在线对战(联机)初始化逻辑
java
synchronized public void onlinePK() {
aiMode = false; // 关闭AI模式,切换为联机模式
netMode = true; // 开启网络联机模式标识
mapIndex = 2; // 设置联机对战对应的地图
playerName = new String(strRead);
myTurn = Integer.valueOf(strRead); // 从服务端获取自己的回合编号
strRead = null;
// 若回合编号为-1,说明连接失败
if (myTurn == -1) {
JOptionPane.showMessageDialog(null, "连接失败");
return;
}
playerName = enemyName; // 记录对手昵称
messageGot = false; // 重置消息接收标识
loadMap(); // 加载联机地图
showMenu = false; // 隐藏游戏菜单
reinit(); // 重新初始化游戏状态
}
-
synchronized修饰:保障多线程环境下(如服务端异步返回数据),该方法不会被并发执行,避免模式/参数混乱; -
reinit():重置游戏核心状态,让联机双方从统一的初始状态开始对战,保障公平性。
消息发送机制(emitString() 方法)
联机对战的核心是"状态变更即发消息",当当前玩家执行关键操作(移动、使用道具、瞄准、发射)时,通过emitString()将操作信息发送给对手,让对手同步更新本地状态。
1. 方法核心实现
java
public void emitString(String mode, String str)
{
System.out.println(mode + "@" + playerName + "@" + str);
String Str = mode + "@" + playerName + "@" + str;
platform_interface.clientSocket.sendGameMsg(Str); // 调用底层通信接口发送消息
}
- 消息格式设计:采用「模式@玩家名@内容」的分隔格式,便于对手解析时快速提取关键信息。
- 底层依赖 :依赖
platform_interface.clientSocket的sendGameMsg()方法实现网络传输,该方法封装了TCP/UDP通信的细节,对外提供简单的字符串发送接口。
示例:发射炮弹时的消息发送
java
public void fire()
{
if (fireable) // 仅当可发射时执行
{
// 联机模式下,当前玩家回合发送三连消息(角度+力度+发射)
if (netMode && turns == myTurn)
{
emitString("DDTANK", "A" + calAngle);
emitString("DDTANK", "P" + power);
emitString("DDTANK", "FIRE");
}
// 后续炮弹初始化逻辑...
}
}
三连消息确保对手能完整同步炮弹发射的核心参数(角度、力度),从而还原一致的炮弹轨迹。
消息解析机制(parseMessage() 方法)
方法核心流程
java
// 角度消息解析
else if (strRead.substring(0, 1).equals("A"))
{
calAngle = Double.valueOf(strRead.substring(1));
System.out.println("angel: " + calAngle);
messageGot = false;
}
// 力度消息解析
else if (strRead.substring(0, 1).equals("P"))
{
power = Integer.valueOf(strRead.substring(1));
System.out.println("power: " + power);
messageGot = false;
}
// 发射消息解析
else if (strRead.equals("FIRE"))
{
roles[turns].fire();
firstPressed = false;
}
- 核心目的:同步对手的瞄准角度、炮弹力度,最终触发本地的炮弹发射方法,还原对手的炮弹飞行轨迹,保证双方看到的爆炸效果、伤害结算一致。
四、课程设计感想
- 通过 Java Socket 套接字编程实现客户端与服务端的双向通信,支撑用户登录状态校验、实时聊天等社交核心功能,解决跨主机的信息交互问题,实现小型桌面社交平台的网络互通能力。
- 新增多角色选择功能,配置不同角色的属性(血量、伤害、移动速度)差异化;基于数据库(MySQL)存储玩家对战战绩与胜率。