你好,这里是《微光之镜》

你好,这里是《微光之镜》

【本介绍更新于2018-12-17,文中的视频与图片均为开发中演示,不代表最终品质】


游戏介绍:

《微光之镜》是一款2D卷轴动作游戏,游戏中你将操作主角Shiro去修正一个被破坏的世界。在这个世界中你会探索到各种风格的场景,遇到不同性格的人物、用丰富的战术去对战不同类型的怪物。

在游戏的设计上我们最重视的有三点:故事叙述与整体情感氛围人物性格刻画多变的游戏玩法

故事叙述与情感氛围将会通过代入感很强的场景美术以及表现力活泼生动的剧本演出小剧场来体现。

    【场景实际演出GIF】

        游戏中将会出现数个个性特征十分鲜明的角色,在与他们的互动中你会进入每个人物的小故事中,作为朋友或者作为敌人来逐渐地了解他们,同时也逐渐了解你自己。

【角色设定草稿图】

        游戏的玩法是以技能道具为核心的组合玩法,你所收集到的技能道具均可以按照你的喜好进行装备,通过你自己的组合产生具有你自己风格的战术。

【测试用的攻击演示(未完成品)】


背景故事

我们的主角Shiro所在的世界是一个什么都没有的世界,整个世界被雪覆盖,除了雪,什么都没有。因为没有任何事物,所以Shiro本身也没有什么情感。

有一天她就许了个愿,希望这个世界能发生一些变化。第二天醒来,她摸到雪地里有一个硬硬的东西。从雪中挖出来后,她发现这是一面神奇的镜子,而这面镜子里面有一个彩色的世界,这个世界竟然可以随着Shiro的想象发生改变。

更神奇的是,当她对镜子里的世界感到满意的时候,镜子里就会传来各种声音,有风吹鸟鸣,还有人们的欢笑声。Shiro感到非常地新奇和快乐,可是当她想把这份快乐分享给别人的时候,却想起这个世界只有她一个人,她第一次感觉到失落。

久而久之,Shiro感受到了一次又一次的快乐和失落,终于,她对这面镜子产生了一种厌恶和愤怒。“如果不是它,我也不会感到这么难过!”一气之下,Shiro举起镜子并狠狠地砸在了地上,镜子被摔的粉碎。

令Shiro没想到的是,随着镜子的破碎,镜子里传出了山崩海啸,动物惊慌逃窜,人们惊叫哭泣的声音。Shiro顿时感到非常懊悔和自责,因为是自己破坏了自己亲手创造的一个非常美好的世界。

这时不知何处传来了声音:“如果你真的后悔的话,那就请用你自己的双手,去修正这个世界,同时去拯救你自己吧。”

Shiro醒来后,发现自己来到了一个没见过的世界,这里有草、有树、有着各种事物,但是也有着许多怪异的景象,而Shiro的冒险,也就此开始。


以上就是对我们制作的独立游戏《微光之镜》的所有介绍了,我们的制作团队为一名全职开发+两名兼职美术的三人团队:【程序】踢踢打踢踢 【美术】黑眼圈很深的CiCi 、【美术】阳子moomin

目前游戏的开发支出仅使用我们三人工作积累的存款,游戏的开发周期将会很长,因此决定在爱发电创建我们的赞助页面,如果有幸你能喜欢我们的游戏,欢迎您成为我们的赞助者,我们也将与赞助者们分享我们的制作进度和小心得,也会不定期放出高清的CG图等来回馈赞助者们(详情可参见主页的赞助档位介绍)。

如果您还是学生党且零用钱有限,请您节省下来用在自己的学业和生活中,在官方微博我的B站主页上贡献一个转发评论,我们就能感受到您珍贵的心意!

【本介绍更新于2018-12-17,文中的视频与图片均为开发中演示,不代表最终品质】

独立游戏《Shiro’s Fantasy World》开坑演示

原创独立游戏《Shiro’s Fantasy World》的开坑演示,制作团队由一名全职程序(我)与两名兼职美术大佬组成。演示中的内容都是开发中或者草稿的阶段,还有非常多的地方等待改进。无论如何,希望大家能喜欢我们的游戏,我们也会努力用心地做出一款让自己自豪的游戏。 如果你有任何建议或者意见,欢迎在视频弹幕和评论中和我们分享。

主角原画草稿图:

Shiro
Kuro

如何使Unity窗口透明置顶,且鼠标可以穿透点击

这里需要感谢给予我极大参考帮助的两个帖子:

https://forum.unity.com/threads/solved-windows-transparent-window-with-opaque-contents-lwa_colorkey.323057/
https://answers.unity.com/questions/869378/viewing-desktop-in-scene.html

可以实现如图的效果:

可以实现的置顶效果演示图

实现透明置顶窗口的代码如下:

using System;
using System.Runtime.InteropServices;
using UnityEngine;

public class TransparentWindow : MonoBehaviour
{
    [SerializeField]
    private Material m_Material;

    private struct MARGINS
    {
        public int cxLeftWidth;
        public int cxRightWidth;
        public int cyTopHeight;
        public int cyBottomHeight;
    }

    [DllImport("user32.dll")]
    private static extern IntPtr GetActiveWindow();

    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

    [DllImport("Dwmapi.dll")]
    private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);

  [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
  private static extern int SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int cx, int cy, int uFlags);

  [DllImport("user32.dll")]
  static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

  [DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
  static extern int SetLayeredWindowAttributes(IntPtr hwnd, int crKey, byte bAlpha, int dwFlags);

  [DllImport("User32.dll")]
  private static extern bool SetForegroundWindow(IntPtr hWnd);

  const int GWL_STYLE = -16;
  const int GWL_EXSTYLE = -20;
  const uint WS_POPUP = 0x80000000;
    const uint WS_VISIBLE = 0x10000000;

  const uint WS_EX_TOPMOST = 0x00000008;
  const uint WS_EX_LAYERED = 0x00080000;
  const uint WS_EX_TRANSPARENT = 0x00000020;

  const int SWP_FRAMECHANGED = 0x0020;
  const int SWP_SHOWWINDOW = 0x0040;
  const int LWA_ALPHA = 2;

  private IntPtr HWND_TOPMOST = new IntPtr(-1);

  private IntPtr _hwnd;

  void Start()
    {
#if !UNITY_EDITOR
    MARGINS margins = new MARGINS() { cxLeftWidth = -1 };
    _hwnd = GetActiveWindow();
    int fWidth = Screen.width;
    int fHeight = Screen.height;

        SetWindowLong(_hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
    SetWindowLong(_hwnd, GWL_EXSTYLE, WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT);
        DwmExtendFrameIntoClientArea(_hwnd, ref margins);
        SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, fWidth, fHeight, SWP_FRAMECHANGED | SWP_SHOWWINDOW); 
        ShowWindowAsync(_hwnd, 3); //Forces window to show in case of unresponsive app    // SW_SHOWMAXIMIZED(3)
#endif
  }

  void OnRenderImage(RenderTexture from, RenderTexture to)
    {
        Graphics.Blit(from, to, m_Material);
    }
}
这里需要给挂上TransparentWindow脚本的主摄像机的脚本处挂上一个material,并使用以下的Shader实现背景的透明:
Shader "Custom/MakeTransparent" {
  Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _TransparentColorKey ("Transparent Color Key", Color) = (0,1,0,1)
    _TransparencyMargin ("Transparency Margin", Float) = 0.01 
  }
  SubShader {
    Pass {
      Tags { "RenderType"="Opaque" }
      LOD 200
    
      CGPROGRAM

      #pragma vertex VertexShaderFunction
      #pragma fragment PixelShaderFunction
    
      #include "UnityCG.cginc"

      struct VertexData
      {
        float4 position : POSITION;
        float2 uv : TEXCOORD0;
      };

      struct VertexToPixelData
      {
        float4 position : SV_POSITION;
        float2 uv : TEXCOORD0;
      };

      VertexToPixelData VertexShaderFunction(VertexData input)
      {
        VertexToPixelData output;
        output.position = UnityObjectToClipPos (input.position);
        output.uv = input.uv;
        return output;
      }
    
      sampler2D _MainTex;
      float3 _TransparentColorKey;
      float _TransparencyMargin;

      float4 PixelShaderFunction(VertexToPixelData input) : SV_Target
      {
        float4 color = tex2D(_MainTex, input.uv);
      
        float deltaR = abs(color.r - _TransparentColorKey.r);
        float deltaG = abs(color.g - _TransparentColorKey.g);
        float deltaB = abs(color.b - _TransparentColorKey.b);

        if (deltaR < _TransparencyMargin && deltaG < _TransparencyMargin && deltaB < _TransparencyMargin)
        {
          return float4(0.0f, 0.0f, 0.0f, 0.0f);
        }

        return color;
      }
      ENDCG
    }
  }
}

6小时自制足球解压游戏——《踢飞守门员》

踢踢一直以来都非常敬佩那些参加GameJam的大佬们,自己不确定自己是否有能力在48小时内做出有创意的优秀独立游戏作品,但是一直都很想体验一下这种氛围。

于是我决定以后不定期地开坑,用周五做视频的时间(5-8小时)来制作奇奇怪怪的小游戏,这期就是我制作的第一款小游戏了!

游戏的玩法是用移动鼠标改变球的位置,点击鼠标射门,目标是踢飞守门员,一共有30关,连续踢中守门员30次就通关啦~

游戏的下载地址:https://pan.baidu.com/s/1Ak7Qyx1oEtbQzTlBuRptZQ

制作过程和试玩视频(6月23日20:00后才能播放哦):

游戏截图简介:

游戏的开始菜单

自定义守门员

游戏规则

点击鼠标踢飞守门员

震惊!4000元改造房间最后竟然还赚了10万?(卧室改造下篇)

离卧室改造上篇(av22358419)已经过去快两个月了,卧室终于改造完成!花费了4000元最后竟然反而还赚了10万多?一起来一探究竟吧!

改造前:

改造后:

视频中的屏幕切换黑科技的传送门在这里:http://r.cwbeta.com/?id=1

沙发的传送门在这里:http://r.cwbeta.com/?id=13

福利!和公主殿下接吻是什么体验?

和公主殿下亲吻是一种什么体验?踢踢用Unity帮你实现!

VR版视频和动态壁纸的获取请关注我的微博:@踢踢打踢踢 ,搜索“公主殿下”就可以找到哦~

VR版百度网盘:http://r.cwbeta.com/?id=9

动态壁纸百度网盘:http://r.cwbeta.com/?id=10

也可以直接在WallPaper的Steam创意工坊使用:https://steamcommunity.com/sharedfiles/filedetails/?id=1400801577

B站评论抽奖Javascript代码

《200多元的树莓派能给你的生活带来多大变化》的这一期视频中,踢踢使用了自己写的一个javascript脚本来实现评论抽奖,有不少小伙伴想要抽奖脚本的源代码,这里我就分享出来,简单地解释一下原理。

每一次点击B站视频下方的评论页码时都会看到有一条jsonp请求发出,如图:

双击打开后发现请求的地址为:https://api.bilibili.com/x/v2/reply?callback=jQuery172009350743824178642_1526364237871&jsonp=jsonp&pn=3&type=1&oid=22995513&sort=0&_=1526364249684

分析一下就可以得出,请求地址的格式为:https://api.bilibili.com/x/v2/reply?callback=<callback函数名>&jsonp=jsonp&pn=<评论页码>&type=1&oid=<视频AV号>&sort=<排序方式>&_=<时间戳>

由于B站做了请求防跨域处理,因此打算简单地用javascript代码贴入B站的console来执行(其实可以写成一个chrome插件,但是我懒,打我呀~)

由于jsonp不支持同步拉取数据,因此会导致大批量拉取评论,有些内容会丢失,因此将jsonp请求嵌套,一个请求succ的处理中再调用下一个请求,保证所有请求按序且完整地收到。

以下就是源代码,在console中执行后,只需要执行StartDraw(开始页码, 结束页码, 评论筛选开始时间, 评论筛选结束时间, 抽奖人数)即可抽出结果,

var userData = {};
var startPage = 0;
var endPage = 0;
var startTime = "";
var endTime = "";
var pickSum = 0;

var dateItem = new Date();

function StartDraw(_startPage, _endPage, _startTime, _endTime, _pickSum)
{
  startPage = _startPage;
  endPage = _endPage;
  startTime = _startTime;
  endTime = _endTime;
  pickSum = _pickSum;
  GetUserData();
}

function GetUserData()
{
  GetOneData(startPage, endPage);
}

function callback(data)
{

}

function GetOneData(page, maxPage)
{
  urlLocationParams = window.location.pathname.split("/");
  avID = urlLocationParams[urlLocationParams.length - 1].split("av")[1];
  var url = "https://api.bilibili.com/x/v2/reply?callback=callback&jsonp=jsonp&pn=" + page + "&type=1&oid=" + avID + "&sort=0&_=" + dateItem.getTime();
  $.ajax({
    url: url,
    type: 'get',
    dataType: 'jsonp',
    jsonpCallback: 'callback',
    success: function (data) {
      console.log(data);
      for (var j = 0; j < data.data.replies.length; j++) {
        var reply = data.data.replies[j];
        if (reply.ctime >= get_unix_time_stamp(startTime)
          && reply.ctime < get_unix_time_stamp(endTime)) {
          userData[reply.mid] = {};
          userData[reply.mid].uid = reply.mid;
          userData[reply.mid].floor = reply.floor;
          userData[reply.mid].name = reply.member.uname;
          userData[reply.mid].content = reply.content.message;
        }
      }
      page += 1;
      if (page <= maxPage)
      {
        GetOneData(page, maxPage)
      }
      else
      {
        PickUser();
      }
    }
  });
}

function PickUser()
{
  userList = [];
  for (var user in userData)
  {
    userList.push(user);
  }
  shuffle(userList);
  for (var i = 0; i < pickSum; i++)
  {
    console.log(userData[userList[i]])
  }
}


function get_unix_time_stamp(strtime) 
{
  if (strtime) {
    var date = new Date(strtime);
  } else {
    var date = new Date();
  }
  time = date.getTime() / 1000;

  return time;
}

function shuffle(array) {
  length = array.length;
  for (var index = 0; index < array.length; index++)
  {
    var temp = array[index];
    var changeIndex = RandomNumBoth(0, length - 1);
    array[index] = array[changeIndex];
    array[changeIndex] = temp;
  }
}

function RandomNumBoth(Min,Max){
  var Range = Max - Min;
  var Rand = Math.random();
  var num = Min + Math.round(Rand * Range); 
  return num;
}

 

200多元的树莓派能给家里带来多大的改变?

200多元的树莓派,这辈子只能吃一次(?),究竟可以给生活增加多厉害的功能呢?我们一起来看吧!
本期视频抽出了上一期视频评论中幸运的五位小伙伴送出小礼品,抽奖过程在视频的最后哦~

 

视频中使用到的几个教程:

树莓派(Raspberry Pi 3) – 系统烧录及系统使用

树莓派 Raspberry Pi 更换国内源

在树莓派上制作开机自启动程序及服务 

树莓派新版系统上使用mjpg-streamer获取USB摄像头和树莓派专用摄像头RaspiCamera图像

Samba服务器(多用户组、多用户有不同的访问权限)

 

视频中的树莓派和配件的购买地址: