iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >如何用Silverlight开发贪吃蛇游戏
  • 754
分享到

如何用Silverlight开发贪吃蛇游戏

2023-06-17 09:06:54 754人浏览 独家记忆
摘要

今天就跟大家聊聊有关如何用Silverlight开发贪吃蛇游戏,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。介绍使用 Silverlight 3.0(C#) 开发一个贪吃蛇

今天就跟大家聊聊有关如何用Silverlight开发贪吃蛇游戏,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

介绍

使用 Silverlight 3.0(C#) 开发一个贪吃蛇游戏

玩法

W/S/A/D 或 ↑/↓/←/→ 控制蛇的移动

截图

如何用Silverlight开发贪吃蛇游戏

思路

贪吃蛇的每一段为 16×16 像素,场景为 640×480 像素,也就说网格为 40×30 个,每个网格的边长为 16

食物的出现位置以及贪吃蛇的运动方向的改变都要在相关的网格内进行

贪吃蛇的运动用即时运算的方法计算,当贪吃蛇运动到网格内(蛇某一段的像素位置%网格的边长<蛇在某时间单位下的移动偏移量)时做如下工作:修正蛇的位置使其正好在网格内,更新蛇的每一段的运动方向,判断是否吃到了食物、是否发生了碰撞等

贪吃蛇的每一段的运动方向的修改:蛇头的运动方向根据用户的操作改变,蛇的每一段的运动方向设置为此段的前一段的运动方向(计算时要从尾部向头部逐段计算)(注:运动方向的改变要在蛇移动到网格内时进行。其中如果蛇的某一段移动到了网格内,则表明其它各段都在网格内)

下面我们看看他的关键代码都有哪些。

关键代码

using System;  using System.Collections.Generic;  using System.Linq;  using System.net;  using System.windows;  using System.Windows.Controls;  using System.Windows.Documents;  using System.Windows.Input;  using System.Windows.Media;  using System.Windows.Media.Animation;  using System.Windows.Shapes;  using System.Windows.Media.Imaging;  using System.Threading;  namespace YYSnake.Core  {      public partial class Main : UserControl      {          private int _columns; // 网格列数          private int _rows; // 网格行数          private Dictionary<Body, CellPoint> _bodies = new Dictionary<Body, CellPoint>(); // 贪吃蛇每一段的集合          private Dictionary<Bean, CellPoint> _beans = new Dictionary<Bean, CellPoint>(); // 豆的集合          private Dictionary<Skin, CellPoint> _skins = new Dictionary<Skin, CellPoint>(); // 蜕下来的皮的集合          private List<CellPoint> _emptyCells = new List<CellPoint>(); // 空网格的集合          private bool _enabled = false; // 游戏是否运行          private double _dt = 0.01; // 多少毫秒计算一次          private int _decimals = 1; // 计算小数时所保留的小数位          private double _speed = 80; // 蛇的运行速度          private Direction _moveDirection = Direction.Up; // 蛇的运行方向          private int _selfLength = 5; // 蛇的最小长度          private int _beansCount = 5; // 豆的***出现数量          private int _ateCapacity = 10; // 食量(超过则蜕皮)          private bool _needRaiseAteEvent = false; // 在“蛇头所处位置进入了网格点区域内”时是否需要触发吃豆事件          private int _needBeansCount = 0; // 还需要增加的豆的数量          Random _random = new Random();          public Main()          {              InitializeComponent();              this.Loaded += new RoutedEventHandler(Main_Loaded);          }          void Main_Loaded(object sender, RoutedEventArgs e)          {              this.Width = App.Width; // 640              this.Height = App.Height; // 480               _columns = (int)(Width / App.CellSize); // 40              _rows = (int)(Height / App.CellSize); // 30               // 防止动画飞出去              RectangleGeometry rg = new RectangleGeometry();              rg.Rect = new Rect(0, 0, App.Width, App.Height);              LayoutRoot.Clip = rg;              bg.Width = App.Width;              bg.Height = App.Height;              ripple.RippleBackground = bg;              CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);          }          /// <summary>          /// 初始化          /// </summary>          public void Init()          {              _enabled = false;              canvasBean.Children.Clear();              canvasSnake.Children.Clear();              canvasSkin.Children.Clear();              _beans.Clear();              _bodies.Clear();              _skins.Clear();              _emptyCells.Clear();              for (int i = 0; i < _columns; i++)              {                  for (int j = 0; j < _rows; j++)                  {                      _emptyCells.Add(new CellPoint(i, j));                  }              }              _moveDirection = Direction.Up;               InitSnake();          }           /// <summary>          /// 蛇的初始化          /// </summary>          private void InitSnake()          {              InitHead();               for (int i = 0; i < _selfLength - 1; i++)                  AddTail();               for (int i = 0; i < _beansCount; i++)                  AddBean();          }          /// <summary>          /// 蛇头的初始化          /// </summary>          private void InitHead()          {              Body head = new Body(BodyType.Head);              head.MoveDirection = _moveDirection;              CellPoint point = new CellPoint((int)(_columns / 2), (int)(_rows / 2));              head.SetValue(Canvas.LeftProperty, point.X * App.CellSize);              head.SetValue(Canvas.TopProperty, point.Y * App.CellSize);              _bodies.Add(head, point);              canvasSnake.Children.Add(head);          }          /// <summary>          /// 增加一个尾巴          /// </summary>          private void AddTail()          {              var prevBody = _bodies.Last().Key;              var prevBodyPoint = _bodies.Last().Value;              Body tail = new Body(BodyType.Tail);              tail.MoveDirection = prevBody.MoveDirection;              CellPoint tailPoint = new CellPoint(prevBodyPoint.X, prevBodyPoint.Y);              switch (prevBody.MoveDirection)              {                  case Direction.Up:                      tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty));                      tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty) + App.CellSize);                      tailPoint.Y++;                      break;                  case Direction.Down:                      tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty));                      tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty) - App.CellSize);                      tailPoint.Y--;                      break;                  case Direction.Left:                      tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty) + App.CellSize);                      tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty));                      tailPoint.X++;                      break;                  case Direction.Right:                      tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty) - App.CellSize);                      tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty));                      tailPoint.X--;                      break;              }               tailPoint = CorrectCellPoint(tailPoint);              _bodies.Add(tail, tailPoint);              canvasSnake.Children.Add(tail);          }          /// <summary>          /// 增加一个豆          /// </summary>          private void AddBean()          {              if (_needBeansCount < _beansCount)                  _needBeansCount++;          }          private DateTime _prevAddBeanDateTime = DateTime.Now;          /// <summary>          /// 生成豆          /// </summary>          void UpdateBean()          {              if (_needBeansCount > 0 && (DateTime.Now - _prevAddBeanDateTime).TotalSeconds > 3)              {                  List<CellPoint> emptyCells = GetEmptyCells();                  if (emptyCells.Count == 0)                  {                      GameOver(this, EventArgs.Empty);                      return;                  }                  CellPoint point = emptyCells[_random.Next(0, emptyCells.Count)];                  Bean bean = new Bean();                  bean.SetValue(Canvas.LeftProperty, point.X * App.CellSize);                  bean.SetValue(Canvas.TopProperty, point.Y * App.CellSize);                  _beans.Add(bean, point);                  canvasBean.Children.Add(bean);                  bean.ani.Completed += delegate                  {                      ripple.ShowRipple(new Point(point.X * App.CellSize + App.CellSize / 2, point.Y * App.CellSize + App.CellSize / 2));                      player.PlayDrop();                  };                  _needBeansCount--;                  _prevAddBeanDateTime = DateTime.Now;              }          }          private DateTime _prevDateTime = DateTime.Now;          private double _leftoverLength = 0d;          void CompositionTarget_Rendering(object sender, EventArgs e)          {              double length = (DateTime.Now - _prevDateTime).TotalSeconds + _leftoverLength;              while (length > _dt)              {                  Update();                  length -= _dt;              }              _leftoverLength = length;              _prevDateTime = DateTime.Now;          }          /// <summary>          /// 即时计算          /// </summary>          private void Update()          {              if (!_enabled)                  return;              double offset = Math.Round(_speed * _dt, _decimals);              // 蛇头所处位置进入了网格点区域内              if (Math.Abs(Math.Round((double)_bodies.First().Key.GetValue(Canvas.TopProperty) % App.CellSize, _decimals)) < offset && Math.Abs(Math.Round((double)_bodies.First().Key.GetValue(Canvas.LeftProperty) % App.CellSize, _decimals)) < offset)              {                  UpdateDirection();                  CorrectPosition();                  UpdateBodyCell();                  CheckEat();                  CheckSkin();                  CheckCollision();                  UpdateBean();                  if (_needRaiseAteEvent)                  {                      Ate(this.Ate, EventArgs.Empty);                      _needRaiseAteEvent = false;                  }              }              UpdatePosition();          }          /// <summary>          /// 蜕皮          /// </summary>          private void CheckSkin()          {              if (_bodies.Count >= _ateCapacity + _selfLength)                  AddSkin(_ateCapacity);          }          /// <summary>          /// 碰撞检测          /// </summary>          private void CheckCollision()          {              if (_skins.Any(p => p.Value == _bodies.First().Value) || _bodies.Where(p => p.Key.BodyType == BodyType.Tail).Any(p => p.Value == _bodies.First().Value))              {                  _enabled = false;                  player.PlayOver();                   GameOver(this, EventArgs.Empty);              }          }          /// <summary>          /// 吃豆          /// </summary>          private void CheckEat()          {              // 是否有被吃的豆              var bean = _beans.FirstOrDefault(p => p.Value == _bodies.First().Value).Key;              if (bean != null)              {                  _beans.Remove(bean);                  canvasBean.Children.Remove(bean);                  player.PlayEat();                  AddTail();                  AddBean();                  _needRaiseAteEvent = true;              }          }          /// <summary>          /// 更新蛇的每一段的运动方向          /// </summary>          private void UpdateDirection()          {              for (int i = _bodies.Count - 1; i > -1; i--)              {                  if (i == 0)                      _bodies.ElementAt(i).Key.MoveDirection = _moveDirection;                  else                     _bodies.ElementAt(i).Key.MoveDirection = _bodies.ElementAt(i - 1).Key.MoveDirection;              }          }          /// <summary>          /// 更新蛇的每一段的位置          /// </summary>          private void UpdatePosition()          {              double offset = Math.Round(_speed * _dt, _decimals);              foreach (var body in _bodies.Keys)              {                  if (body.MoveDirection == Direction.Up)                      body.SetValue(Canvas.TopProperty, Math.Round((double)body.GetValue(Canvas.TopProperty) - offset, _decimals));                  else if (body.MoveDirection == Direction.Down)                      body.SetValue(Canvas.TopProperty, Math.Round((double)body.GetValue(Canvas.TopProperty) + offset, _decimals));                  else if (body.MoveDirection == Direction.Left)                      body.SetValue(Canvas.LeftProperty, Math.Round((double)body.GetValue(Canvas.LeftProperty) - offset, _decimals));                  else if (body.MoveDirection == Direction.Right)                      body.SetValue(Canvas.LeftProperty, Math.Round((double)body.GetValue(Canvas.LeftProperty) + offset, _decimals));              }          }          /// <summary>          /// 蜕指定数量的皮          /// </summary>          private void AddSkin(int count)          {              player.PlaySkin();              while (count > 0)              {                  KeyValuePair<Body, CellPoint> body = _bodies.ElementAt(_bodies.Count - 1);                  CellPoint skinPoint = body.Value;                  Skin skin = new Skin();                  skin.SetValue(Canvas.LeftProperty, skinPoint.X * App.CellSize);                  skin.SetValue(Canvas.TopProperty, skinPoint.Y * App.CellSize);                  _skins.Add(skin, skinPoint);                  canvasSkin.Children.Add(skin);                  _emptyCells.Remove(skinPoint);                  canvasSnake.Children.Remove(body.Key);                  _bodies.Remove(body.Key);                  count--;              }          }          #region 辅助方法          /// <summary>          /// 修正指定的位置信息为整数          /// </summary>          private int CorrectPosition(double position)          {              double result;              double offset = Math.Round(_speed * _dt, _decimals);              double temp = Math.Round(position % App.CellSize, _decimals);              if (Math.Abs(temp) < offset)                  result = Math.Round(position - temp);              else                 result = Math.Round(position - temp) + Math.Sign(temp) * App.CellSize;              return (int)result;          }          /// <summary>          /// 修正蛇的每一段的位置为整数          /// </summary>          private void CorrectPosition()          {              foreach (Body body in _bodies.Keys)              {                  double x = CorrectPosition((double)body.GetValue(Canvas.LeftProperty));                  double y = CorrectPosition((double)body.GetValue(Canvas.TopProperty));                   if (x == App.Width)                      x = 0d;                  else if (x == -App.CellSize)                      x = App.Width - App.CellSize;                  else if (y == App.Height)                      y = 0d;                  else if (y == -App.CellSize)                      y = App.Height - App.CellSize;                  body.SetValue(Canvas.LeftProperty, x);                  body.SetValue(Canvas.TopProperty, y);              }          }          /// <summary>          /// 更新蛇的每一段的网格位置信息          /// </summary>          private void UpdateBodyCell()          {              for (int i = 0; i < _bodies.Count; i++)              {                  UpdateBodyCell(_bodies.ElementAt(i).Key);              }          }          /// <summary>          /// 更新指定的 Body 的网格位置信息          /// </summary>          private void UpdateBodyCell(Body body)          {              CellPoint point = new CellPoint((int)((double)body.GetValue(Canvas.LeftProperty) / App.CellSize), (int)((double)body.GetValue(Canvas.TopProperty) / App.CellSize));              if (body.MoveDirection == Direction.Up)                  point.Y--;              else if (body.MoveDirection == Direction.Down)                  point.Y++;              else if (body.MoveDirection == Direction.Left)                  point.X--;              else if (body.MoveDirection == Direction.Right)                  point.X++;               point = CorrectCellPoint(point);               _bodies[body] = point;          }          /// <summary>          /// 修正网格位置          /// </summary>          private CellPoint CorrectCellPoint(CellPoint point)          {              if (point.X > _columns - 1)                  point.X = _columns - point.X;              else if (point.X < 0)                  point.X = point.X + _columns;               if (point.Y > _rows - 1)                  point.Y = _rows - point.Y;              else if (point.Y < 0)                  point.Y = point.Y + _rows;               return point;          }          /// <summary>          /// 获取空网格集合          /// </summary>          private List<CellPoint> GetEmptyCells()          {              List<CellPoint> emptyCells = new List<CellPoint>();              List<CellPoint> aroundHeadCells = new List<CellPoint>();              CellPoint headPoint = _bodies.First().Value;              for (int i = -5; i < 5; i++)              {                  for (int j = -5; j < 5; j++)                  {                      CellPoint point = new CellPoint(headPoint.X + i, headPoint.Y + j);                      point = CorrectCellPoint(point);                       aroundHeadCells.Add(point);                  }              }              // skin 的占位情况因为确定了就不变了,所以在 AddSkin() 处计算              // 为了以下 LINQ 的可用,需要重写 CellPoint 的 public override bool Equals(object obj)              emptyCells = _emptyCells.Where(p => !_bodies.Select(x => x.Value).Contains(p)).ToList();              emptyCells = emptyCells.Where(p => !_beans.Select(x => x.Value).Contains(p)).ToList();              emptyCells = emptyCells.Where(p => !aroundHeadCells.Contains(p)).ToList();              return emptyCells;          }          #endregion          #region 属性          public Direction MoveDirection          {              set              {                  Body head = _bodies.First().Key;                   if (head.MoveDirection == Direction.Up && value == Direction.Down)                      return;                  if (head.MoveDirection == Direction.Down && value == Direction.Up)                      return;                  if (head.MoveDirection == Direction.Left && value == Direction.Right)                      return;                  if (head.MoveDirection == Direction.Right && value == Direction.Left)                      return;                  _moveDirection = value;              }          }          public bool Enabled          {              get { return _enabled; }              set { _enabled = value; }          }          public double Speed          {              get { return _speed; }              set { _speed = value; }          }          public int AteCapacity          {              get { return _ateCapacity; }              set { _ateCapacity = value; }          }          #endregion          #region 事件 GameOver 和 Ate          public event EventHandler GameOver;          public event EventHandler Ate;          #endregion      }  }

看完上述内容,你们对如何用Silverlight开发贪吃蛇游戏有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程网精选频道,感谢大家的支持。

--结束END--

本文标题: 如何用Silverlight开发贪吃蛇游戏

本文链接: https://www.lsjlt.com/news/288268.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何用Silverlight开发贪吃蛇游戏
    今天就跟大家聊聊有关如何用Silverlight开发贪吃蛇游戏,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。介绍使用 Silverlight 3.0(c#) 开发一个贪吃蛇...
    99+
    2023-06-17
  • C#游戏开发之实现贪吃蛇游戏
    目录实践过程效果代码实践过程 效果 代码 public partial class Form1 : Form { public Form1() { ...
    99+
    2023-01-04
    C#实现贪吃蛇游戏 C#贪吃蛇游戏 C#贪吃蛇
  • QT如何实现贪吃蛇游戏
    这篇文章主要介绍了QT如何实现贪吃蛇游戏,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。为了熟悉QT的相关知识,我用了大约8个小时的时间用QT再次写了一遍贪吃蛇。因为QT的机制...
    99+
    2023-06-15
  • python如何实现贪吃蛇游戏
    这篇文章主要介绍了python如何实现贪吃蛇游戏,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。游戏实现效果如下:后面有完整代码和解析import sysimport...
    99+
    2023-06-14
  • js如何实现贪吃蛇游戏
    本篇内容介绍了“js如何实现贪吃蛇游戏”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!两个小时完成的,有点简陋。直接看效果。打开调试面板,在r...
    99+
    2023-06-14
  • C语言实现贪吃蛇小游戏开发
    本文实例为大家分享了C语言实现贪吃蛇小游戏的具体代码,供大家参考,具体内容如下 程序介绍 代码 #include<stdafx.h>            //vc自带...
    99+
    2022-11-13
    C语言 贪吃蛇
  • JavaScript实现贪吃蛇游戏
    本文实例为大家分享了JavaScript实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下 通过JavaScript,我们可以实现贪吃蛇游戏,具体功能如下: (1)通过按上下左右键来...
    99+
    2024-04-02
  • 如何使用HTML5实现贪吃蛇游戏
    这篇文章将为大家详细讲解有关如何使用HTML5实现贪吃蛇游戏,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。 游戏操作说明 通过方向键控制贪吃蛇上下左右移动。贪吃蛇吃到...
    99+
    2024-04-02
  • Java实现贪吃蛇游戏
    下面是一个简单的Java实现贪吃蛇游戏的示例代码:```javaimport javax.swing.*;import java.a...
    99+
    2023-08-09
    Java
  • QT实现贪吃蛇游戏
    为了熟悉QT的相关知识,我用了大约8个小时的时间用QT再次写了一遍贪吃蛇。 因为QT的机制和平时写的程序流程不同,所以程序中可能没有遵守代码规范。 运行效果: 程序内除了实现贪吃蛇...
    99+
    2024-04-02
  • Android 2d游戏开发之贪吃蛇基于surfaceview
    前两个游戏是基于View游戏框架的,View游戏框架只适合做静止的,异步触发的游戏,如果做一直在动的游戏,View的效率就不高了,我们需要一种同步触发的游戏框架,也就是surface...
    99+
    2024-04-02
  • 用JS实现贪吃蛇游戏
    本文实例为大家分享了JS实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下 效果图: 完整代码如下: html: <!DOCTYPE html> <html la...
    99+
    2024-04-02
  • pygame实现贪吃蛇游戏
    本文实例为大家分享了pygame实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下 为了简化起见,游戏素材暂定为两张简单的图片(文中用的是30*30)。大家很方便就能制作。 背景也...
    99+
    2024-04-02
  • 如何利用pygame实现贪吃蛇游戏
    这篇文章主要介绍如何利用pygame实现贪吃蛇游戏,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!创建蛇首先,先分析一下蛇的移动,不然我们一定会吃亏的(别问,问就是自己写了一堆无效代码)。蛇的移动其实并没有想象中那样复...
    99+
    2023-06-15
  • python实现贪吃蛇游戏
    文章目录 1、效果2、实现过程3、代码 1、效果 2、实现过程 导入 Pygame 和 random 模块。初始化 Pygame。设置游戏界面大小、背景颜色和游戏标题。定义颜色常量。...
    99+
    2023-09-29
    python 游戏 pygame
  • 如何实现贪吃蛇Python小游戏
    这篇文章主要介绍“如何实现贪吃蛇Python小游戏”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何实现贪吃蛇Python小游戏”文章能帮助大家解决问题。贪吃蛇Python小游戏(源码+注释+粘贴即...
    99+
    2023-07-05
  • 利用TypeScript编写贪吃蛇游戏
    目录Explanation1. tsconfig.json配置2. HTML & CSS 布局相关3. TS核心逻辑项目源码链接先来康康效果图 我接下来将讲解相关配置和代码...
    99+
    2024-04-02
  • 用JS实现贪吃蛇小游戏
    本文实例为大家分享了JS实现贪吃蛇小游戏的具体代码,供大家参考,具体内容如下 效果图: 完整代码如下: HTML <!DOCTYPE html> <html la...
    99+
    2024-04-02
  • 怎么用Python写贪吃蛇游戏
    怎么用Python写贪吃蛇游戏,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。前几天,有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典...
    99+
    2023-06-02
  • 如何使用JavaScript实现贪吃蛇小游戏
    这篇文章将为大家详细讲解有关如何使用JavaScript实现贪吃蛇小游戏,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。JavaScript实现贪吃蛇小游戏功能概述本程序实...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作