Jump to content

Xzzzx Ru-Amber


Recommended Posts

Немного необычный формат открытки. Я так до конца и не понял как её лучше представить. Дело в том, что открытка сделана в виде программы. Это анимация, в которой можно менять режимы гирлянды!

 

На видео демонстрация каждого режима

 

 

Программа не полноэкранная, поэтому вот так поместил все режимы на 1 видео, чтобы не страдало качество при просмотре на полный экран

 

Ещё я хотел сделать gif, но формат похоже не осилил такую динамику. Были сильные фризы и дёрганья

 

Небольшой обзор того, что понадобилось для создания

Спойлер

Новогодняя ёлка из таверны

image.png.8f8929024d2db7a168c313db396b1802.png

 

Вырезал её около часа, а затем подкрутил экспозицию, тк правая часть в тени

 

Скриншот с Предела 2020-2021

image.thumb.png.a743c6b72c504d4fa63df057304fa6dc.png

 

До сих пор это моё самое любимое событие в игре. Мы практически с самого начала Предела заняли 20 рб, не оставив ни единого шанса Хранителям. Порой выбивали даже небольшие группы высокоуровневых персонажей, рассчитывавших легко закрыть рб для собы ги!

Тут я достаточно быстро вырезал основных участников сего действа

 

Пол из Цитадели Белоборода

image.png.0b49f02ea4a6b68845210465a09412fd.png

 

Вырезал квадратики в качестве бэкграунда

 

300+ строчек кода

Может быть я не важный художник, за то умею программировать. "Так почему бы и нет", подумал я?

 

Edited by XzzzX
Link to comment
Share on other sites

Только что, Nebaluika сказал:

На чём писал программу?

PascalABC.NET

 

Я вот сейчас как раз думаю как бы дать возможность форумчанам запустить это дело

@Dr Strange знаю, что на форум нельзя вот так просто выложить exe, но может быть как-то можно сделать исключение? Или мне лучше полностью расписать как собрать программу самостоятельно? Там не слишком сложно и думаю все разберутся, но вероятно это слишком муторно для людей вне IT

Link to comment
Share on other sites

Posted (edited)

Инструкция по запуску

Спойлер

Понадобится Windows (желательно 10 или 11) и PascalABC.NET StandardPack с официального сайта

 

Скачайте следующие картинки

Спойлер

background.png

image.png.0ee5bd90100d39cd5adccb62d2f4f7b1.png

 

rogues.png

image.png.e819f1afa907be105f295f183ad7f433.png

 

tree.png

image.png.fad1ccc9da2e438356f9474c81a485f3.png

 

Создайте 2 файла в той же папке, что и скачанные картинки. Содержимое файлов:

WarspearPostcard.pas

Цитата

uses System.Windows;
uses GraphWPF, GraphWPFBase, WPFObjects, SceneUnit;

begin
  Invoke( ()-> (MainWindow.ResizeMode:= ResizeMode.CanMinimize) );
  Window.SetSize(495, 279);
  Window.Title:= 'Письмо от XzzzX';
  
  //заполнение фона
  for var i:= 0 to 3 do
    for var j:= 0 to 1 do
      new PictureWPF(i*216, j*216, 'background.png');
  
  //статические картинки
  new PictureWPF(40, 150, 'rogues.png');
  new PictureWPF(350, 130, 192*0.5, 288*0.5, 'tree.png');
  
  //коллекция лампочек гирлянды  
  var radius:= 20;
  var bulbs:= new List<Bulb>;
  bulbs.AddRange(
    |
      new Bulb(67, 170, radius),
      new Bulb(117, 245, radius),
      new Bulb(164, 170, radius),
      new Bulb(210, 245, radius),
      new Bulb(262, 170, radius),
      new Bulb(40, 40, radius),
      new Bulb(80, 100, radius),
      new Bulb(120, 30, radius),
      new Bulb(440, 30, radius),
      new Bulb(250, 35, radius),
      new Bulb(470, 200, radius),
      new Bulb(340, 190, radius),
      new Bulb(320, 250, radius)
    |
  );
  
  //цветовая схема
  var colorMap:= new Dictionary<GColor, GColor>;
  colorMap[Colors.Red]:= Colors.Green;
  colorMap[Colors.Green]:= Colors.Yellow;
  colorMap[Colors.Yellow]:= Colors.Blue;
  colorMap[Colors.Blue]:= Colors.Red;
  
  var garland:= new Garland(
    bulbs,
    new ColorScheme(colorMap)
  );
  
  var scaleFactor:= 1.08;
  var warspearText:= new AnimatedText(
    new Point(340, 55),
    'Warspear Online',
    20,
    Colors.Yellow,
    500,
    text-> begin
      text.AnimScale(scaleFactor, 0.5);
      scaleFactor:= 1/scaleFactor;
    end
  );
  warspearText.Rotate(25);
  
  var progress:= 0.0;
  var newYearText:= new AnimatedText(
    new Point(110, 70),
    'С Новым Годом и',
    35,
    Colors.Yellow,
    40,
    text-> begin
      text.Color:= SmootchColorTransition(Colors.Yellow, Colors.Cyan, progress);
      progress+= 0.03;
      if progress >= 1 then
        progress:= 0;
    end
  );
  
  var ChristmasText:= new AnimatedText(
    new Point(153, 105),
    'Рождеством',
    35,
    Colors.Yellow,
    40,
    text-> begin
      text.Color:= SmootchColorTransition(Colors.Yellow, Colors.Cyan, progress);
    end
  );
  
  var scene:= new Scene(
    garland,
    warspearText,
    newYearText,
    ChristmasText
  );
  
  OnKeyUp:= k-> begin
    case k of 
      Key.D1: scene.Garland.Mode:= GarlandMode.Fast;
      Key.D2: scene.Garland.Mode:= GarlandMode.Slow;
      Key.D3: scene.Garland.Mode:= GarlandMode.PWM;
    end;
  end;
end.

 

SceneUnit.pas

Цитата

unit SceneUnit;

interface
uses System, System.Windows.Threading;
uses GraphWPF, GraphWPFBase, WPFObjects;

type
  ColorScheme = class
  private
    _map: Dictionary<GColor, GColor>;
  public
    function FirstColor: GColor;
    function NextColor(current: GColor): Gcolor;
    
    constructor(map: Dictionary<GColor, GColor>); 
  end;

  AnimatedText = class (TextWPF)
  private
    _timer: DispatcherTimer;
  public
    constructor(pos: Point; text: string; size: real; color: GColor; time: integer; callback: Action<AnimatedText>);
  end;
  
  Bulb = class
  private
    _circle: CircleWPF;
  public
    property Color: GColor read _circle.Color write _circle.Color:= value;
    
    constructor(x, y: real; radius: real);
  end;
  
  GarlandMode = (Fast, Slow, PWM);
  
  Garland = class
  private
    _bulbs: List<Bulb>;
    _mode: GarlandMode;
    _colorScheme: ColorScheme;
    _timer: DispatcherTimer;
    _timerCallback: EventHandler;
    
    procedure SetColorScheme(value: ColorScheme);
    procedure SetGarlandMode(value: GarlandMode);
    
    procedure RestoreColors;
  public
    property ColorScheme: SceneUnit.ColorScheme read _colorScheme write SetColorScheme;
    property Mode: GarlandMode read _mode write SetGarlandMode;
    property Item[i: integer]: Bulb read _bulbs;
    
    constructor(bulbs: sequence of Bulb; colorScheme: SceneUnit.ColorScheme);
  end;

  Scene = class
  public
    Garland: Garland;
    WarspearText: AnimatedText;
    NewYearText: AnimatedText;
    ChristmasText: AnimatedText;
    constructor(garland: SceneUnit.Garland; warspearText, newYearText, ChristmasText: AnimatedText);
  end;

function SmootchColorTransition(current, target: GColor; progress: real): GColor;

implementation
///Плавное переливание одного цвета в другой
function SmootchColorTransition(current, target: GColor; progress: real): GColor;
begin
  result:= GColor.FromArgb(
    integer(current.A*(1-progress) + target.A * progress),
    integer(current.R*(1-progress) + target.R * progress),
    integer(current.G*(1-progress) + target.G * progress),
    integer(current.B*(1-progress) + target.B * progress)
  );
end;

{$region ColorScheme}
function ColorScheme.FirstColor: GColor;
begin
  result:= _map.First.Value;
end;

function ColorScheme.NextColor(current: GColor): Gcolor;
begin
  result:= _map[current];
end;

constructor ColorScheme.Create(map: Dictionary<GColor, GColor>);
begin
  _map:= map;
end;
{$endregion}

{$region AnimatedText}
constructor AnimatedText.Create(pos: Point; text: string; size: real; color: GColor; time: integer; callback: Action<AnimatedText>);
begin
  inherited Create(pos.X, pos.Y, size, text, color);
  
  _timer:= new DispatcherTimer(DispatcherPriority.Normal, MainWindow.Dispatcher);
  _timer.Interval:= TimeSpan.FromMilliseconds(time);
  _timer.Tick+= (sender, ea)-> begin callback(self) end;
  _timer.Start;
end;
{$endregion}

{$region Bulb}
constructor Bulb.Create(x, y: real; radius: real);
begin
  _circle:= new CircleWPF(x, y, radius, RandomColor);
end;
{$endregion}

{$region Garland}
procedure Garland.RestoreColors;
begin
  _timer.Stop;
  
  var color:= ColorScheme.FirstColor;
  foreach var item in _bulbs do begin
    item.Color:= color;
    color:= ColorScheme.NextColor(color);
  end;
  
  _timer.Start;
end;

procedure Garland.SetColorScheme(value: SceneUnit.ColorScheme);
begin
  _colorScheme:= value;
  RestoreColors;
end;

procedure Garland.SetGarlandMode(value: GarlandMode);
begin
  _mode:= value;
  
  _timer.Stop;
  _timer.Tick-= _timerCallback;
  
  RestoreColors;
  
  if value = GarlandMode.Fast then begin
    _timer.Interval:= TimeSpan.FromMilliseconds(750);
    
    _timerCallback:= (sender, ea)-> begin
      foreach var item in _bulbs do
        item.Color:= ColorScheme.NextColor(item.Color);
      end;
    _timer.Tick+= _timerCallback;
  end
  else if value = GarlandMode.Slow then begin
    _timer.Interval:= TimeSpan.FromMilliseconds(30);
    
    var currentColors:= _bulbs.Select(s-> s.Color).ToArray;
    var targetColors:= currentColors.Select( s-> _colorScheme.NextColor(s) ).ToArray;
    
    var progress:= 0.0;
    var skipCount:= 20;
    
    _timerCallback:= (sender, ea)-> begin
      if skipCount > 0 then begin
        skipCount-= 1;
        exit;
      end;
      
      foreach var item in _bulbs index i do
        item.Color:= SmootchColorTransition(currentColors, targetColors, progress);
      
      progress+= 0.05;
      if progress >= 1 then begin
        currentColors:= targetColors;
        targetColors:= currentColors.Select( s-> _colorScheme.NextColor(s) ).ToArray;
        progress:= 0;
        skipCount:= 20;
      end;
    end;
    
    _timer.Tick+= _timerCallback;
  end
  else if value = GarlandMode.PWM then begin
    _timer.Interval:= TimeSpan.FromMilliseconds(40);
    
    var currentColors:= _bulbs.Select(s-> s.Color).ToArray;
    var targetColors:= ArrGen(
      _bulbs.Count, 
      i-> GColor.FromArgb(0, currentColors.R,  currentColors.G, currentColors.B)
    );
      
    var progress:= 0.0;
    var skipCount:= 20;
    
    _timerCallback:= (sender, ea)-> begin
      if skipCount > 0 then begin
        skipCount-= 1;
        exit;
      end;
      
      foreach var item in _bulbs index i do
        item.Color:= SmootchColorTransition(currentColors, targetColors, progress);
      
      progress+= 0.05;
      if progress >= 1 then begin
        swap(currentColors, targetColors);
        progress:= 0;
        skipCount:= 20;
      end;
    end;
    
    _timer.Tick+= _timerCallback;    
  end;
  
  _timer.Start;
end;

constructor Garland.Create(bulbs: sequence of Bulb; colorScheme: SceneUnit.ColorScheme);
begin
  _bulbs:= new List<Bulb>(bulbs);
  _timer:= new DispatcherTimer(DispatcherPriority.Normal, MainWindow.Dispatcher);
  
  self.ColorScheme:= colorScheme;
  Mode:= GarlandMode.Fast;
end;
{$endregion}

{$region Scene}
constructor Scene.Create(garland: SceneUnit.Garland; warspearText, newYearText, ChristmasText: AnimatedText);
begin  
  self.Garland:= garland;
  self.WarspearText:= warspearText;
  self.NewYearText:= newYearText;
  self.ChristmasText:= ChristmasText;  
end;
{$endregion}

begin end.

Откройте WarspearPostcard.pas в PascalABC.NET и нажмите кнопку запуска (зелёная стрелка)

 

Режимы гирлянды переключаются цифрами 1, 2 и 3

 

Edited by XzzzX
Link to comment
Share on other sites

38 минут назад, XzzzX сказал:

Инструкция по запуску

На следующем конкурсе будет ссылка на торрент с пробелами?:yum1:

Link to comment
Share on other sites

Posted (edited)

Переделал видео. Теперь качество практически как в исходном варианте

Edited by XzzzX
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...